]> git.saurik.com Git - apple/launchd.git/commitdiff
launchd-842.1.4.tar.gz os-x-109 os-x-1091 v842.1.4
authorApple <opensource@apple.com>
Tue, 29 Oct 2013 00:03:35 +0000 (00:03 +0000)
committerApple <opensource@apple.com>
Tue, 29 Oct 2013 00:03:35 +0000 (00:03 +0000)
25 files changed:
launchd.xcodeproj/project.pbxproj
liblaunch/launch.h
liblaunch/launch_internal.h
liblaunch/launch_priv.h
liblaunch/libbootstrap.c
liblaunch/liblaunch.c
liblaunch/libvproc.c
liblaunch/vproc_priv.h
man/launchd.plist.5
src/config.h
src/core.c
src/core.h
src/ipc.c
src/job.defs
src/job_types.defs
src/launchd.c
src/log.c
src/log.h
src/runtime.c
src/runtime.h
support/launchctl.c
xcconfigs/launchctl.xcconfig
xcconfigs/launchd.xcconfig
xcconfigs/liblaunch.xcconfig
xcsupport/osx_redirect_name [deleted file]

index c424676b524754380dfa57a941b88e30eef019cb..408a2a2bf72cc5fd00ccefebd3db0eed0bcf3103 100644 (file)
@@ -88,7 +88,6 @@
                60416D1F1402EE7300C190AA /* domain.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4B0FB8E91241FE3F00383109 /* domain.defs */; settings = {ATTRIBUTES = (Server, ); }; };
                7215DE4C0EFAF2EC00ABD81E /* libauditd.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 7215DE4B0EFAF2EC00ABD81E /* libauditd.dylib */; };
                726055EC0EA7EC2400D65FE7 /* mach_exc.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC36291F0E9349410054F1A3 /* mach_exc.defs */; settings = {ATTRIBUTES = (Server, ); }; };
-               726056090EA7FCF200D65FE7 /* ktrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB15D0EA7D7B200B2AC84 /* ktrace.c */; };
                72AFE8090EFAF3D9004BDA46 /* libauditd.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 7215DE4B0EFAF2EC00ABD81E /* libauditd.dylib */; };
                72FDB15F0EA7D7B200B2AC84 /* ktrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB15D0EA7D7B200B2AC84 /* ktrace.c */; };
                72FDB1C00EA7E21C00B2AC84 /* job_forward.defs in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB1BF0EA7E21C00B2AC84 /* job_forward.defs */; };
                4B949DD4143D010F008712B9 /* libCrashReporterClient.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libCrashReporterClient.a; path = usr/local/lib/libCrashReporterClient.a; sourceTree = SDKROOT; };
                4B9EDCA10EAFC77E00A78496 /* DiskArbitration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DiskArbitration.framework; path = /System/Library/Frameworks/DiskArbitration.framework; sourceTree = "<absolute>"; };
                4BA2F5FC1243063D00C2AADD /* init.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = init.defs; path = usr/local/include/xpc/init.defs; sourceTree = SDKROOT; };
-               4BC868A8143CFD0300B46F40 /* osx_redirect_name */ = {isa = PBXFileReference; lastKnownFileType = text; name = osx_redirect_name; path = xcsupport/osx_redirect_name; sourceTree = "<group>"; };
                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 = src/config.h; sourceTree = "<group>"; };
                72FDB15D0EA7D7B200B2AC84 /* ktrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ktrace.c; path = src/ktrace.c; sourceTree = "<group>"; };
                4BC868A6143CFC8C00B46F40 /* xcsupport */ = {
                        isa = PBXGroup;
                        children = (
-                               4BC868A8143CFD0300B46F40 /* osx_redirect_name */,
                        );
                        name = xcsupport;
                        sourceTree = "<group>";
                FC59A03F0E8C87FD00D41150 /* Project object */ = {
                        isa = PBXProject;
                        attributes = {
-                               LastUpgradeCheck = 0450;
+                               LastUpgradeCheck = 0500;
                        };
                        buildConfigurationList = FC59A0420E8C87FD00D41150 /* Build configuration list for PBXProject "launchd" */;
                        compatibilityVersion = "Xcode 3.2";
                                FC59A0F40E8C8AA600D41150 /* liblaunch.c in Sources */,
                                FC59A0F00E8C8AA600D41150 /* libvproc.c in Sources */,
                                FC59A0F70E8C8AA600D41150 /* libbootstrap.c in Sources */,
-                               726056090EA7FCF200D65FE7 /* ktrace.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                FC59A0410E8C87FD00D41150 /* Release */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
-                               COMBINE_HIDPI_IMAGES = YES;
                                COPY_PHASE_STRIP = YES;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                DEAD_CODE_STRIPPING = YES;
index aadc62f52a2f00eab5f79bc222cc3999337ebfc3..07ea8b791cbca8336882a032d3502ea70d33396b 100644 (file)
@@ -52,6 +52,8 @@ __BEGIN_DECLS
 #define LAUNCH_KEY_GETJOBS "GetJobs"
 #define LAUNCH_KEY_CHECKIN "CheckIn"
 
+#define LAUNCH_JOBKEY_DEFAULTS "__Defaults"
+
 #define LAUNCH_JOBKEY_LABEL "Label"
 #define LAUNCH_JOBKEY_DISABLED "Disabled"
 #define LAUNCH_JOBKEY_USERNAME "UserName"
@@ -105,6 +107,13 @@ __BEGIN_DECLS
 #define LAUNCH_JOBKEY_IGNOREPROCESSGROUPATSHUTDOWN     "IgnoreProcessGroupAtShutdown"
 #define LAUNCH_JOBKEY_POLICIES "Policies"
 #define LAUNCH_JOBKEY_ENABLETRANSACTIONS "EnableTransactions"
+#define LAUNCH_JOBKEY_CFBUNDLEIDENTIFIER "CFBundleIdentifier"
+#define LAUNCH_JOBKEY_PROCESSTYPE "ProcessType"
+#define LAUNCH_KEY_PROCESSTYPE_APP "App"
+#define LAUNCH_KEY_PROCESSTYPE_STANDARD "Standard"
+#define LAUNCH_KEY_PROCESSTYPE_BACKGROUND "Background"
+#define LAUNCH_KEY_PROCESSTYPE_INTERACTIVE "Interactive"
+#define LAUNCH_KEY_PROCESSTYPE_ADAPTIVE "Adaptive"
 
 #define LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS "DenyCreatingOtherJobs"
 
index aa6576f8e2772a9866e92d1d3f30d80304b38b0f..a78d4bd4277b62a6163bb4ce19dc598ae6725e0e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2007-2012 Apple Inc. All rights reserved.
  *
  * @APPLE_APACHE_LICENSE_HEADER_START@
  * 
 #ifndef __LAUNCH_INTERNAL_H__
 #define __LAUNCH_INTERNAL_H__
 
+#include "vproc_priv.h"
+
 #include <paths.h>
+#include <dispatch/dispatch.h>
+#include <pthread.h>
+
+#if __has_include(<os/alloc_once_private.h>)
+#include <os/alloc_once_private.h>
+#if defined(OS_ALLOC_ONCE_KEY_LIBLAUNCH)
+#define _LIBLAUNCH_HAS_ALLOC_ONCE 1
+#endif
+#endif
+
+typedef struct _launch *launch_t;
+
+struct launch_globals_s {
+       // liblaunch.c
+       pthread_once_t lc_once;
+       pthread_mutex_t lc_mtx;
+       launch_t l;
+       launch_data_t async_resp;
+       
+       launch_t in_flight_msg_recv_client;
+       
+       int64_t s_am_embedded_god;
+       
+       // libvproc.c
+       dispatch_queue_t _vproc_gone2zero_queue;
+       _vproc_transaction_callout _vproc_gone2zero_callout;
+       void *_vproc_gone2zero_ctx;
+       
+       dispatch_once_t _vproc_transaction_once;
+       uint64_t _vproc_transaction_enabled;
+       dispatch_queue_t _vproc_transaction_queue;
+       int64_t _vproc_transaction_cnt;
+};
+typedef struct launch_globals_s *launch_globals_t;
+
+void _launch_init_globals(launch_globals_t globals);
+
+#if !_LIBLAUNCH_HAS_ALLOC_ONCE
+launch_globals_t _launch_globals_impl(void);
+#endif
+
+__attribute__((__pure__))
+static inline launch_globals_t
+_launch_globals(void) {
+#if _LIBLAUNCH_HAS_ALLOC_ONCE
+       return (launch_globals_t)os_alloc_once(OS_ALLOC_ONCE_KEY_LIBLAUNCH,
+                                              sizeof(struct launch_globals_s),
+                                              (void*)&_launch_init_globals);
+#else
+       return _launch_globals_impl();
+#endif
+}
 
 #pragma GCC visibility push(default)
 
 #define LAUNCHD_DB_PREFIX "/private/var/db/launchd.db"
 #define LAUNCHD_LOG_PREFIX "/private/var/log"
 
-typedef struct _launch *launch_t;
+#define LAUNCHD_JOB_DEFAULTS "Defaults"
+#define LAUNCHD_JOB_DEFAULTS_CACHED "CachedDefaults"
 
 launch_t launchd_fdopen(int, int);
 int launchd_getfd(launch_t);
index 878101bae10de0d254ab98c2e57e1519e8056992..518cf958effd72f48f7bb766b1ca8f81b8f93fd7 100644 (file)
@@ -32,6 +32,8 @@
 
 __BEGIN_DECLS
 
+#define LAUNCH_EXITSTATUS_FAIRPLAY_FAIL (INT64_MAX)
+
 #define LAUNCH_KEY_SETUSERENVIRONMENT "SetUserEnvironment"
 #define LAUNCH_KEY_UNSETUSERENVIRONMENT "UnsetUserEnvironment"
 #define LAUNCH_KEY_SHUTDOWN "Shutdown"
@@ -40,7 +42,6 @@ __BEGIN_DECLS
 #define LAUNCH_KEY_SETRESOURCELIMITS "SetResourceLimits"
 #define LAUNCH_KEY_GETRUSAGESELF "GetResourceUsageSelf"
 #define LAUNCH_KEY_GETRUSAGECHILDREN "GetResourceUsageChildren"
-#define LAUNCH_KEY_SETPRIORITYLIST "SetPriorityList"
 
 #define LAUNCHD_SOCKET_ENV "LAUNCHD_SOCKET"
 #define LAUNCHD_SOCK_PREFIX _PATH_VARTMP "launchd"
@@ -54,9 +55,11 @@ __BEGIN_DECLS
 #define LAUNCH_JOBKEY_SANDBOXPROFILE "SandboxProfile"
 #define LAUNCH_JOBKEY_SANDBOXFLAGS "SandboxFlags"
 #define LAUNCH_JOBKEY_SANDBOX_NAMED "Named"
+#define        LAUNCH_JOBKEY_SANDBOXCONTAINER "SandboxContainer"
 #define LAUNCH_JOBKEY_JETSAMPROPERTIES "JetsamProperties"
 #define LAUNCH_JOBKEY_JETSAMPRIORITY "JetsamPriority"
 #define LAUNCH_JOBKEY_JETSAMMEMORYLIMIT "JetsamMemoryLimit"
+#define LAUNCH_JOBKEY_JETSAMMEMORYLIMITBACKGROUND "JetsamMemoryLimitBackground"
 #define LAUNCH_JOBKEY_SECURITYSESSIONUUID "SecuritySessionUUID"
 #define LAUNCH_JOBKEY_DISABLEASLR "DisableASLR"
 #define LAUNCH_JOBKEY_XPCDOMAIN "XPCDomain"
@@ -68,13 +71,16 @@ __BEGIN_DECLS
 #define LAUNCH_KEY_JETSAMPRIORITY LAUNCH_JOBKEY_JETSAMPRIORITY
 #define LAUNCH_KEY_JETSAMMEMORYLIMIT LAUNCH_JOBKEY_JETSAMMEMORYLIMIT
 
+#define LAUNCH_KEY_POSIXSPAWNTYPE_APP LAUNCH_KEY_PROCESSTYPE_APP
+#define LAUNCH_KEY_POSIXSPAWNTYPE_SYSTEMAPP "SystemApp"
+#define LAUNCH_KEY_POSIXSPAWNTYPE_STANDARD LAUNCH_KEY_PROCESSTYPE_STANDARD
+#define LAUNCH_KEY_POSIXSPAWNTYPE_BACKGROUND LAUNCH_KEY_PROCESSTYPE_BACKGROUND
+#define LAUNCH_KEY_POSIXSPAWNTYPE_INTERACTIVE LAUNCH_KEY_PROCESSTYPE_INTERACTIVE
+#define LAUNCH_KEY_POSIXSPAWNTYPE_ADAPTIVE LAUNCH_KEY_PROCESSTYPE_ADAPTIVE
 #define LAUNCH_KEY_POSIXSPAWNTYPE_TALAPP "TALApp"
-#define LAUNCH_KEY_POSIXSPAWNTYPE_WIDGET "Widget"
-#define LAUNCH_KEY_POSIXSPAWNTYPE_IOSAPP "iOSApp"
-#define LAUNCH_KEY_POSIXSPAWNTYPE_BACKGROUND "Background"
-#define LAUNCH_KEY_POSIXSPAWNTYPE_INTERACTIVE "Interactive"
 
 #define LAUNCH_JOBKEY_EMBEDDEDPRIVILEGEDISPENSATION "EmbeddedPrivilegeDispensation"
+#define LAUNCH_JOBKEY_EMBEDDEDHOMESCREEN "EmbeddedHomeScreen"
 #define LAUNCH_JOBKEY_EMBEDDEDMAINTHREADPRIORITY "EmbeddedMainThreadPriority"
 
 #define LAUNCH_JOBKEY_ENTERKERNELDEBUGGERBEFOREKILL "EnterKernelDebuggerBeforeKill"
@@ -87,15 +93,21 @@ __BEGIN_DECLS
 #define LAUNCH_JOBKEY_SHUTDOWNMONITOR "ShutdownMonitor"
 #define LAUNCH_JOBKEY_BEGINTRANSACTIONATSHUTDOWN "BeginTransactionAtShutdown"
 #define LAUNCH_JOBKEY_XPCDOMAINBOOTSTRAPPER "XPCDomainBootstrapper"
+#define LAUNCH_JOBKEY_ASID "AuditSessionID"
+#define LAUNCH_JOBKEY_JOINGUISESSION "JoinGUISession"
 
 #define LAUNCH_JOBKEY_MACH_KUNCSERVER "kUNCServer"
 #define LAUNCH_JOBKEY_MACH_EXCEPTIONSERVER "ExceptionServer"
 #define LAUNCH_JOBKEY_MACH_TASKSPECIALPORT "TaskSpecialPort"
 #define LAUNCH_JOBKEY_MACH_HOSTSPECIALPORT "HostSpecialPort"
 #define LAUNCH_JOBKEY_MACH_ENTERKERNELDEBUGGERONCLOSE "EnterKernelDebuggerOnClose"
+#define LAUNCH_JOBKEY_LOWPRIORITYBACKGROUNDIO "LowPriorityBackgroundIO"
 
 #define LAUNCH_ENV_INSTANCEID "LaunchInstanceID"
 
+#define JETSAM_PROPERTY_PRIORITY "Priority"
+#define JETSAM_PROPERTY_MEMORYLIMIT "MemoryLimitMB"
+
 /* For LoginWindow.
  *
  * After this call, the task's bootstrap port is set to the per session launchd.
index c9f85adcf6d5f5baad820e617c38b3964abe63a3..251664920e17352b42693bcc3263cc57169ad127 100644 (file)
@@ -36,8 +36,6 @@
 
 #include "job.h"
 
-mach_port_t    bootstrap_port = MACH_PORT_NULL;
-
 void
 bootstrap_init(void)
 {
@@ -137,6 +135,7 @@ kern_return_t
 bootstrap_check_in(mach_port_t bp, const name_t service_name, mach_port_t *sp)
 {
        uuid_t junk;
+       (void)bzero(junk, sizeof(junk));
        return vproc_mig_check_in2(bp, (char *)service_name, sp, junk, 0);
 }
 
@@ -144,6 +143,7 @@ kern_return_t
 bootstrap_check_in2(mach_port_t bp, const name_t service_name, mach_port_t *sp, uint64_t flags)
 {
        uuid_t junk;
+       (void)bzero(junk, sizeof(junk));
        return vproc_mig_check_in2(bp, (char *)service_name, sp, junk, flags);
 }
 
index 0afdf0a0fe7bdd3eeada07757c205ce400552139..6cef3dd26cdc93b30418b706c40b34b74cb2e55b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2012 Apple Inc. All rights reserved.
  *
  * @APPLE_APACHE_LICENSE_HEADER_START@
  * 
@@ -196,17 +196,32 @@ static void launch_msg_getmsgs(launch_data_t m, void *context);
 static launch_data_t launch_msg_internal(launch_data_t d);
 static void launch_mach_checkin_service(launch_data_t obj, const char *key, void *context);
 
-static int64_t s_am_embedded_god = false;
-static launch_t in_flight_msg_recv_client;
-static pthread_once_t _lc_once = PTHREAD_ONCE_INIT;
+void
+_launch_init_globals(launch_globals_t globals)
+{
+       pthread_once_t once = PTHREAD_ONCE_INIT;
+       globals->lc_once = once;
+       pthread_mutex_init(&globals->lc_mtx, NULL);
+}
 
-bool launchd_apple_internal = false;
+#if !_LIBLAUNCH_HAS_ALLOC_ONCE
+launch_globals_t __launch_globals;
+
+void
+_launch_globals_init(void)
+{
+       __launch_globals = calloc(1, sizeof(struct launch_globals_s));
+       _launch_init_globals(__launch_globals);
+}
 
-static struct _launch_client {
-       pthread_mutex_t mtx;
-       launch_t        l;
-       launch_data_t   async_resp;
-} *_lc = NULL;
+launch_globals_t
+_launch_globals_impl(void)
+{
+       static pthread_once_t once = PTHREAD_ONCE_INIT;
+       pthread_once(&once, &_launch_globals_init);
+       return __launch_globals;
+}
+#endif
 
 void
 launch_client_init(void)
@@ -217,13 +232,6 @@ launch_client_init(void)
        int dfd, lfd = -1, cifd = -1;
        name_t spath;
 
-       _lc = calloc(1, sizeof(struct _launch_client));
-
-       if (!_lc)
-               return;
-
-       pthread_mutex_init(&_lc->mtx, NULL);
-
        if (_launchd_fd) {
                cifd = strtol(_launchd_fd, NULL, 10);
                if ((dfd = dup(cifd)) >= 0) {
@@ -262,15 +270,16 @@ launch_client_init(void)
                }
        }
 
+       launch_globals_t globals = _launch_globals();
        if ((lfd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) {
                goto out_bad;
        }
 
 #if TARGET_OS_EMBEDDED
-       (void)vproc_swap_integer(NULL, VPROC_GSK_EMBEDDEDROOTEQUIVALENT, NULL, &s_am_embedded_god);
+       (void)vproc_swap_integer(NULL, VPROC_GSK_EMBEDDEDROOTEQUIVALENT, NULL, &globals->s_am_embedded_god);
 #endif
        if (-1 == connect(lfd, (struct sockaddr *)&sun, sizeof(sun))) {
-               if (cifd != -1 || s_am_embedded_god) {
+               if (cifd != -1 || globals->s_am_embedded_god) {
                        /* There is NO security enforced by this check. This is just a hint to our
                         * library that we shouldn't error out due to failing to open this socket. If
                         * we inherited a trusted file descriptor, we shouldn't fail. This should be
@@ -282,27 +291,26 @@ launch_client_init(void)
                        goto out_bad;
                }
        }
-
-       if (!(_lc->l = launchd_fdopen(lfd, cifd))) {
+       
+       if (!(globals->l = launchd_fdopen(lfd, cifd))) {
                goto out_bad;
        }
 
-       if (!(_lc->async_resp = launch_data_alloc(LAUNCH_DATA_ARRAY))) {
+       if (!(globals->async_resp = launch_data_alloc(LAUNCH_DATA_ARRAY))) {
                goto out_bad;
        }
 
        return;
 out_bad:
-       if (_lc->l)
-               launchd_close(_lc->l, close);
-       else if (lfd != -1)
+       if (globals->l) {
+               launchd_close(globals->l, close);
+               globals->l = NULL;
+       } else if (lfd != -1) {
                close(lfd);
+       }
        if (cifd != -1) {
                close(cifd);
        }
-       if (_lc)
-               free(_lc);
-       _lc = NULL;
 }
 
 launch_data_t
@@ -665,8 +673,10 @@ out_bad:
 void
 launchd_close(launch_t lh, typeof(close) closefunc)
 {
-       if (in_flight_msg_recv_client == lh) {
-               in_flight_msg_recv_client = NULL;
+       launch_globals_t globals = _launch_globals();
+
+       if (globals->in_flight_msg_recv_client == lh) {
+               globals->in_flight_msg_recv_client = NULL;
        }
 
        if (lh->sendbuf)
@@ -969,14 +979,15 @@ launchd_msg_send(launch_t lh, launch_data_t d)
 int
 launch_get_fd(void)
 {
-       pthread_once(&_lc_once, launch_client_init);
+       launch_globals_t globals = _launch_globals();
+       pthread_once(&globals->lc_once, launch_client_init);
 
-       if (!_lc) {
+       if (!globals->l) {
                errno = ENOTCONN;
                return -1;
        }
 
-       return _lc->l->fd;
+       return globals->l->fd;
 }
 
 void
@@ -984,8 +995,10 @@ launch_msg_getmsgs(launch_data_t m, void *context)
 {
        launch_data_t async_resp, *sync_resp = context;
 
+       launch_globals_t globals = _launch_globals();
+
        if ((LAUNCH_DATA_DICTIONARY == launch_data_get_type(m)) && (async_resp = launch_data_dict_lookup(m, LAUNCHD_ASYNC_MSG_KEY))) {
-               launch_data_array_set_index(_lc->async_resp, launch_data_copy(async_resp), launch_data_array_get_count(_lc->async_resp));
+               launch_data_array_set_index(globals->async_resp, launch_data_copy(async_resp), launch_data_array_get_count(globals->async_resp));
        } else {
                *sync_resp = launch_data_copy(m);
        }
@@ -1055,20 +1068,21 @@ launch_msg_internal(launch_data_t d)
                return resp;
        }
 
-       pthread_once(&_lc_once, launch_client_init);
-       if (!_lc) {
+       launch_globals_t globals = _launch_globals();
+       pthread_once(&globals->lc_once, launch_client_init);
+       if (!globals->l) {
                errno = ENOTCONN;
                return NULL;
        }
 
        int fd2use = -1;
-       if ((launch_data_get_type(d) == LAUNCH_DATA_STRING && strcmp(launch_data_get_string(d), LAUNCH_KEY_CHECKIN) == 0) || s_am_embedded_god) {
-               _lc->l->which = LAUNCHD_USE_CHECKIN_FD;
+       if ((launch_data_get_type(d) == LAUNCH_DATA_STRING && strcmp(launch_data_get_string(d), LAUNCH_KEY_CHECKIN) == 0) || globals->s_am_embedded_god) {
+               globals->l->which = LAUNCHD_USE_CHECKIN_FD;
        } else {
-               _lc->l->which = LAUNCHD_USE_OTHER_FD;
+               globals->l->which = LAUNCHD_USE_OTHER_FD;
        }
 
-       fd2use = launchd_getfd(_lc->l);
+       fd2use = launchd_getfd(globals->l);
 
        if (fd2use == -1) {
                errno = EPERM;
@@ -1115,21 +1129,21 @@ launch_msg_internal(launch_data_t d)
        }
 #endif
 
-       pthread_mutex_lock(&_lc->mtx);
+       pthread_mutex_lock(&globals->lc_mtx);
 
-       if (d && launchd_msg_send(_lc->l, d) == -1) {
+       if (d && launchd_msg_send(globals->l, d) == -1) {
                do {
                        if (errno != EAGAIN)
                                goto out;
-               } while (launchd_msg_send(_lc->l, NULL) == -1);
+               } while (launchd_msg_send(globals->l, NULL) == -1);
        }
 
        while (resp == NULL) {
-               if (d == NULL && launch_data_array_get_count(_lc->async_resp) > 0) {
-                       resp = launch_data_array_pop_first(_lc->async_resp);
+               if (d == NULL && launch_data_array_get_count(globals->async_resp) > 0) {
+                       resp = launch_data_array_pop_first(globals->async_resp);
                        goto out;
                }
-               if (launchd_msg_recv(_lc->l, launch_msg_getmsgs, &resp) == -1) {
+               if (launchd_msg_recv(globals->l, launch_msg_getmsgs, &resp) == -1) {
                        if (errno != EAGAIN) {
                                goto out;
                        } else if (d == NULL) {
@@ -1184,7 +1198,7 @@ out:
        }
 #endif
 
-       pthread_mutex_unlock(&_lc->mtx);
+       pthread_mutex_unlock(&globals->lc_mtx);
 
        return resp;
 }
@@ -1260,12 +1274,14 @@ launchd_msg_recv(launch_t lh, void (*cb)(launch_data_t, void *), void *context)
                        goto out_bad;
                }
 
-               in_flight_msg_recv_client = lh;
+               launch_globals_t globals = _launch_globals();
+
+               globals->in_flight_msg_recv_client = lh;
 
                cb(rmsg, context);
 
                /* launchd and only launchd can call launchd_close() as a part of the callback */
-               if (in_flight_msg_recv_client == NULL) {
+               if (globals->in_flight_msg_recv_client == NULL) {
                        r = 0;
                        break;
                }
index a9151dedb9ab980815fd000d141fca5df96b809c..7e035bdbe4dc7b0e5b0689bc331fff7062c05802 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2012 Apple Inc. All rights reserved.
  *
  * @APPLE_APACHE_LICENSE_HEADER_START@
  * 
@@ -40,7 +40,7 @@
 #include <sys/syscall.h>
 #include <sys/event.h>
 #include <System/sys/fileport.h>
-#include <assumes.h>
+#include <os/assumes.h>
 
 #if HAVE_QUARANTINE
 #include <quarantine.h>
@@ -67,11 +67,6 @@ void _vproc_transactions_enable_internal(void *arg);
 void _vproc_transaction_begin_internal(void *arg __unused);
 void _vproc_transaction_end_internal(void *arg __unused);
 
-static dispatch_once_t _vproc_transaction_once = 0;
-static uint64_t _vproc_transaction_enabled = 0;
-static dispatch_queue_t _vproc_transaction_queue = NULL;
-static int64_t _vproc_transaction_cnt = 0;
-
 #pragma mark vproc Object
 struct vproc_s {
        int32_t refcount;
@@ -127,47 +122,66 @@ vproc_release(vproc_t vp)
 static void
 _vproc_transaction_init_once(void *arg __unused)
 {
+       launch_globals_t globals = _launch_globals();
+
        int64_t enable_transactions = 0;
        (void)vproc_swap_integer(NULL, VPROC_GSK_TRANSACTIONS_ENABLED, 0, &enable_transactions);
        if (enable_transactions != 0) {
-               (void)osx_assumes_zero(proc_track_dirty(getpid(), PROC_DIRTY_TRACK));
-               _vproc_transaction_enabled = 1;
+               (void)os_assumes_zero(proc_track_dirty(getpid(), PROC_DIRTY_TRACK));
+               globals->_vproc_transaction_enabled = 1;
        }
-       _vproc_transaction_queue = dispatch_queue_create("com.apple.idle-exit-queue", NULL);
+       globals->_vproc_transaction_queue = dispatch_queue_create("com.apple.idle-exit-queue", NULL);
 }
 
 void
 _vproc_transactions_enable_internal(void *arg __unused)
 {
-       (void)osx_assumes_zero(proc_track_dirty(getpid(), PROC_DIRTY_TRACK));
-       _vproc_transaction_enabled = 1;
+       launch_globals_t globals = _launch_globals();
+
+       if (!globals->_vproc_transaction_enabled) {
+               (void)os_assumes_zero(proc_track_dirty(getpid(), PROC_DIRTY_TRACK));
+               globals->_vproc_transaction_enabled = 1;
+       }
 
-       if (_vproc_transaction_cnt > 0) {
-               (void)osx_assumes_zero(proc_set_dirty(getpid(), true));
+       if (globals->_vproc_transaction_cnt > 0) {
+               (void)os_assumes_zero(proc_set_dirty(getpid(), true));
        }
 }
 
 void
 _vproc_transactions_enable(void)
 {
-       dispatch_once_f(&_vproc_transaction_once, NULL, _vproc_transaction_init_once);
-       dispatch_sync_f(_vproc_transaction_queue, NULL, _vproc_transactions_enable_internal);
+       launch_globals_t globals = _launch_globals();
+
+       dispatch_once_f(&globals->_vproc_transaction_once, NULL, _vproc_transaction_init_once);
+       dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transactions_enable_internal);
 }
 
 void
 _vproc_transaction_begin_internal(void *ctx __unused)
 {
-       int64_t new = ++_vproc_transaction_cnt;
-       if (new == 1 && _vproc_transaction_enabled) {
-               (void)osx_assumes_zero(proc_set_dirty(getpid(), true));
+       launch_globals_t globals = _launch_globals();
+
+       int64_t new = ++globals->_vproc_transaction_cnt;
+       if (!globals->_vproc_transaction_enabled || new > 1) {
+               return;
+       }
+
+       if (new < 1) {
+               _vproc_set_crash_log_message("Underflow of transaction count.");
+               abort();
        }
+
+       (void)os_assumes_zero(proc_set_dirty(getpid(), true));
 }
 
 void
 _vproc_transaction_begin(void)
 {
-       dispatch_once_f(&_vproc_transaction_once, NULL, _vproc_transaction_init_once);
-       dispatch_sync_f(_vproc_transaction_queue, NULL, _vproc_transaction_begin_internal);
+       launch_globals_t globals = _launch_globals();
+
+       dispatch_once_f(&globals->_vproc_transaction_once, NULL, _vproc_transaction_init_once);
+       dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transaction_begin_internal);
 }
 
 vproc_transaction_t
@@ -178,25 +192,64 @@ vproc_transaction_begin(vproc_t vp __unused)
        /* Return non-NULL on success. Originally, there were dreams of returning
         * an object or something, but those never panned out.
         */
-       return (vproc_transaction_t)vproc_transaction_begin;;
+       return (vproc_transaction_t)vproc_transaction_begin;
 }
 
+void _vproc_transaction_end_flush(void);
+
 void
-_vproc_transaction_end_internal(void *arg __unused)
+_vproc_transaction_end_internal2(void *ctx)
 {
-       int64_t new = --_vproc_transaction_cnt;
-       if (new == 0 && _vproc_transaction_enabled) {
-               (void)osx_assumes_zero(proc_set_dirty(getpid(), false));
-       } else if (new < 0) {
+       launch_globals_t globals = _launch_globals();
+
+       globals->_vproc_gone2zero_callout(ctx);
+       _vproc_transaction_end_flush();
+}
+
+void
+_vproc_transaction_end_internal(void *arg)
+{
+       launch_globals_t globals = _launch_globals();
+
+       int64_t new = --globals->_vproc_transaction_cnt;
+       if (!globals->_vproc_transaction_enabled || new > 0) {
+               return;
+       }
+
+       if (new < 0) {
                _vproc_set_crash_log_message("Underflow of transaction count.");
+               abort();
        }
+
+       if (globals->_vproc_gone2zero_callout && !arg) {
+               globals->_vproc_transaction_cnt = 1;
+               dispatch_async_f(globals->_vproc_gone2zero_queue, globals->_vproc_gone2zero_ctx, _vproc_transaction_end_internal2);
+       } else {
+               (void)os_assumes_zero(proc_set_dirty(getpid(), false));
+       }
+}
+
+void
+_vproc_transaction_end_flush2(void *ctx __unused)
+{
+       _vproc_transaction_end_internal((void *)1);
+}
+
+void
+_vproc_transaction_end_flush(void)
+{
+       launch_globals_t globals = _launch_globals();
+
+       dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transaction_end_flush2);
 }
 
 void
 _vproc_transaction_end(void)
 {
-       dispatch_once_f(&_vproc_transaction_once, NULL, _vproc_transaction_init_once);
-       dispatch_sync_f(_vproc_transaction_queue, NULL, _vproc_transaction_end_internal);
+       launch_globals_t globals = _launch_globals();
+
+       dispatch_once_f(&globals->_vproc_transaction_once, NULL, _vproc_transaction_init_once);
+       dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transaction_end_internal);
 }
 
 void
@@ -208,7 +261,9 @@ vproc_transaction_end(vproc_t vp __unused, vproc_transaction_t vpt __unused)
 size_t
 _vproc_transaction_count(void)
 {
-       return _vproc_transaction_cnt;
+       launch_globals_t globals = _launch_globals();
+
+       return globals->_vproc_transaction_cnt;
 }
 
 size_t
@@ -264,14 +319,14 @@ _vproc_transaction_count_for_pid(pid_t p, int32_t *count, bool *condemned)
                        error = ret;
                }
        }
-       
        return error;
 }
 void
 _vproc_transaction_try_exit(int status)
 {
 #if !TARGET_OS_EMBEDDED
-       if (_vproc_transaction_cnt == 0) {
+       launch_globals_t globals = _launch_globals();
+       if (globals->_vproc_transaction_cnt == 0) {
                _exit(status);
        }
 #else
@@ -297,21 +352,17 @@ _vproc_standby_end(void)
 
 }
 
-/* TODO: obsoleted - remove post-build submission */
-
-int32_t *
-_vproc_transaction_ptr(void)
-{
-       static int32_t dummy = 1;
-       return &dummy;
-}
-
 void
-_vproc_transaction_set_callouts(_vproc_transaction_callout gone2zero __unused, _vproc_transaction_callout gonenonzero __unused)
+_vproc_transaction_set_clean_callback(dispatch_queue_t targetq, void *ctx, dispatch_function_t func)
 {
-}
+       launch_globals_t globals = _launch_globals();
 
-/* */
+       globals->_vproc_gone2zero_queue = targetq;
+       dispatch_retain(targetq);
+
+       globals->_vproc_gone2zero_callout = func;
+       globals->_vproc_gone2zero_ctx = ctx;
+}
 
 void
 vproc_standby_end(vproc_t vp __unused, vproc_standby_t vpt __unused)
@@ -428,39 +479,18 @@ _vprocmgr_detach_from_console(vproc_flags_t flags __attribute__((unused)))
 vproc_err_t
 _vproc_post_fork_ping(void)
 {
-#if !TARGET_OS_EMBEDDED
-       au_asid_t s = AU_DEFAUDITSID;
-       do {
-               mach_port_t session = MACH_PORT_NULL;
-               kern_return_t kr = vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session);
-               if (kr != KERN_SUCCESS) {
-                       /* If this happens, our bootstrap port probably got hosed. */
-                       _vproc_log(LOG_ERR, "Post-fork ping failed!");
-                       break;
-               }
-
-               /* If we get back MACH_PORT_NULL, that means we just stick with the session
-                * we inherited across fork(2).
-                */
-               if (session == MACH_PORT_NULL) {
-                       s = ~AU_DEFAUDITSID;
-                       break;
-               }
+       mach_port_t session = MACH_PORT_NULL;
+       kern_return_t kr = vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session);
+       if (kr) {
+               return _vproc_post_fork_ping;
+       }
 
-               s = _audit_session_join(session);
-               if (s == 0) {
-                       _vproc_log_error(LOG_ERR, "Could not join security session!");
-                       s = AU_DEFAUDITSID;
-               } else {
-                       _vproc_log(LOG_DEBUG, "Joined session %d.", s);
-               }
-       } while (0);
+       if (session) {
+               (void)_audit_session_join(session);
+               (void)mach_port_deallocate(mach_task_self(), session);
+       }
 
-       return s != AU_DEFAUDITSID ? NULL : _vproc_post_fork_ping;
-#else
-       mach_port_t session = MACH_PORT_NULL;
-       return vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session) ? _vproc_post_fork_ping : NULL;
-#endif
+       return NULL;
 }
 
 vproc_err_t
@@ -556,10 +586,6 @@ _spawn_via_launchd(const char *label, const char *const *argv, const struct spaw
                        tmp = launch_data_new_string(LAUNCH_KEY_POSIXSPAWNTYPE_TALAPP);
                        launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_POSIXSPAWNTYPE);
                }
-               if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_WIDGET) {
-                       tmp = launch_data_new_string(LAUNCH_KEY_POSIXSPAWNTYPE_WIDGET);
-                       launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_POSIXSPAWNTYPE);
-               }
                if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_DISABLE_ASLR) {
                        tmp = launch_data_new_bool(true);
                        launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_DISABLEASLR);
@@ -955,7 +981,7 @@ union maxmsgsz {
        union __ReplyUnion__helper_downcall_launchd_helper_subsystem rep;
 };
 
-size_t vprocmgr_helper_maxmsgsz = sizeof(union maxmsgsz);
+const size_t vprocmgr_helper_maxmsgsz = sizeof(union maxmsgsz);
 
 kern_return_t
 helper_recv_wait(mach_port_t p, int status)
index 954b4810f974f4019d258204fcb0b4a8a845c008..8d3b0e516b1562d724410ba22acc69e89d86bad8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2006-2012 Apple Inc. All rights reserved.
  *
  * @APPLE_APACHE_LICENSE_HEADER_START@
  * 
@@ -31,6 +31,7 @@
 #include <vproc.h>
 #include <uuid/uuid.h>
 #include <servers/bootstrap.h>
+#include <dispatch/dispatch.h>
 
 #ifndef VPROC_HAS_TRANSACTIONS
 #define VPROC_HAS_TRANSACTIONS 1
@@ -54,7 +55,7 @@ __BEGIN_DECLS
 /* DO NOT use this. This is a hack for 'launchctl' */
 #define VPROC_MAGIC_UNLOAD_SIGNAL 0x4141504C
 
-typedef void (*_vproc_transaction_callout)(void);
+typedef void (*_vproc_transaction_callout)(void *);
 
 typedef enum {
        VPROC_GSK_ZERO,
@@ -208,11 +209,9 @@ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA)
 size_t
 _vproc_transaction_count(void);
 
-int32_t *
-_vproc_transaction_ptr(void);
-
 void
-_vproc_transaction_set_callouts(_vproc_transaction_callout gone2zero, _vproc_transaction_callout gonenonzero);
+_vproc_transaction_set_clean_callback(dispatch_queue_t targetq, void *ctx,
+       dispatch_function_t func);
 
 void
 _vproc_transactions_enable(void);
index 068f56a1073f56e1391626758a0ace028c646704..f7f5a090a233cd23f7d77bd62aa0c678b4550113 100644 (file)
@@ -338,6 +338,29 @@ performed automatically by the system.
 This optional key specifies what
 .Xr nice 3
 value should be applied to the daemon.
+.It Sy ProcessType <string>
+This optional key describes, at a high level, the intended purpose of the job.
+The system will apply resource limits based on what kind of job it is. If left
+unspecified, the system will apply light resource limits to the job, throttling
+its CPU usage and I/O bandwidth. The following are valid values:
+.Bl -ohang -offset indent
+.It Sy Background
+Background jobs are generally processes that do work that was not directly
+requested by the user. The resource limits applied to Background jobs are
+intended to prevent them from disrupting the user experience.
+.It Sy Standard
+Standard jobs are equivalent to no ProcessType being set.
+.It Sy Adaptive
+Adaptive jobs move between the Background and Interactive classifications based
+on activity over XPC connections. See
+.Xr xpc_transaction_begin 3
+for details.
+.It Sy Interactive
+Interactive jobs run with the same resource limitations as apps, that is to say,
+none. Interactive jobs are critical to maintaining a responsive user experience,
+and this key should only be used if an app's ability to be responsive depends
+on it, and cannot be made Adaptive.
+.El
 .It Sy AbandonProcessGroup <boolean>
 When a job dies,
 .Nm launchd
index 841f524b68f5745979bde7e3f64e7d5880e8b02c..30848724316d7b9f316e3b89733aa988cd5725df 100644 (file)
@@ -9,6 +9,12 @@
 #define HAVE_QUARANTINE 0
 #endif
 
+#if __has_include(<responsibility.h>)
+#define HAVE_RESPONSIBILITY 1
+#else
+#define HAVE_RESPONSIBILITY 0
+#endif
+
 #if __has_include(<sandbox.h>)
 #define HAVE_SANDBOX 1
 #else
 
 #define HAVE_LIBAUDITD !TARGET_OS_EMBEDDED
 
+#if !TARGET_OS_EMBEDDED && __has_include(<systemstats/systemstats.h>)
+#define HAVE_SYSTEMSTATS 1
+#else
+#define HAVE_SYSTEMSTATS 0
+#endif
+
 #endif /* __CONFIG_H__ */
index beb8e2106a5a353399efd2d6c46a5b2e0bb827c1..e5720e4d4db120221cad456cb8e383af2b8fc285 100644 (file)
 #include <spawn_private.h>
 #include <time.h>
 #include <libinfo.h>
-#include <assumes.h>
+#include <os/assumes.h>
 #include <xpc/launchd.h>
+#include <asl.h>
+#include <_simple.h>
 
 #include <libproc.h>
+#include <libproc_internal.h>
 #include <System/sys/proc_info.h>
 #include <malloc/malloc.h>
 #include <pthread.h>
-#include <libproc.h>
 #if HAVE_SANDBOX
 #define __APPLE_API_PRIVATE
 #include <sandbox.h>
 #if HAVE_QUARANTINE
 #include <quarantine.h>
 #endif
+#if HAVE_RESPONSIBILITY
+#include <responsibility.h>
+#endif
 #if !TARGET_OS_EMBEDDED
 extern int gL1CacheEnabled;
 #endif
+#if HAVE_SYSTEMSTATS
+#include <systemstats/systemstats.h>
+#endif
 
 #include "launch.h"
 #include "launch_priv.h"
@@ -122,6 +130,11 @@ extern int gL1CacheEnabled;
 
 #define POSIX_SPAWN_IOS_INTERACTIVE 0
 
+#if TARGET_OS_EMBEDDED
+/* Default memory highwatermark for daemons as set out in <rdar://problem/10307788>. */
+#define DEFAULT_JETSAM_DAEMON_HIGHWATERMARK 5
+#endif
+
 /* LAUNCHD_DEFAULT_EXIT_TIMEOUT
  *   If the job hasn't exited in the given number of seconds after sending
  *   it a SIGTERM, SIGKILL it. Can be overriden in the job plist.
@@ -139,10 +152,6 @@ extern int gL1CacheEnabled;
 
 #define IS_POWER_OF_TWO(v) (!(v & (v - 1)) && v)
 
-#ifndef NOTE_EXIT_REPARENTED
-#define NOTE_EXIT_REPARENTED 0x00080000
-#endif
-
 extern char **environ;
 
 struct waiting_for_removal {
@@ -176,10 +185,11 @@ struct machservice {
                drain_all_on_crash:1,
                upfront:1,
                event_channel:1,
+               recv_race_hack :1,
                /* Don't let the size of this field to get too small. It has to be large
                 * enough to represent the reasonable range of special port numbers.
                 */
-               special_port_num:18;
+               special_port_num:17;
        const char name[0];
 };
 
@@ -319,6 +329,7 @@ struct externalevent {
        bool wanted_state;
        bool internal;
        xpc_object_t event;
+       xpc_object_t entitlements;
 
        char name[0];
 };
@@ -328,7 +339,7 @@ struct externalevent_iter_ctx {
        struct eventsystem *sys;
 };
 
-static bool externalevent_new(job_t j, struct eventsystem *sys, const char *evname, xpc_object_t event);
+static bool externalevent_new(job_t j, struct eventsystem *sys, const char *evname, xpc_object_t event, uint64_t flags);
 static void externalevent_delete(struct externalevent *ee);
 static void externalevent_setup(launch_data_t obj, const char *key, void *context);
 static struct externalevent *externalevent_find(const char *sysname, uint64_t id);
@@ -346,6 +357,20 @@ static void eventsystem_setup(launch_data_t obj, const char *key, void *context)
 static struct eventsystem *eventsystem_find(const char *name);
 static void eventsystem_ping(void);
 
+struct waiting4attach {
+       LIST_ENTRY(waiting4attach) le;
+       mach_port_t port;
+       pid_t dest;
+       xpc_service_type_t type;
+       char name[0];
+};
+
+static LIST_HEAD(, waiting4attach) _launchd_domain_waiters;
+
+static struct waiting4attach *waiting4attach_new(jobmgr_t jm, const char *name, mach_port_t port, pid_t dest, xpc_service_type_t type);
+static void waiting4attach_delete(jobmgr_t jm, struct waiting4attach *w4a);
+static struct waiting4attach *waiting4attach_find(jobmgr_t jm, job_t j);
+
 #define ACTIVE_JOB_HASH_SIZE 32
 #define ACTIVE_JOB_HASH(x) (IS_POWER_OF_TWO(ACTIVE_JOB_HASH_SIZE) ? (x & (ACTIVE_JOB_HASH_SIZE - 1)) : (x % ACTIVE_JOB_HASH_SIZE))
 
@@ -358,6 +383,7 @@ struct jobmgr_s {
        SLIST_ENTRY(jobmgr_s) sle;
        SLIST_HEAD(, jobmgr_s) submgrs;
        LIST_HEAD(, job_s) jobs;
+       LIST_HEAD(, waiting4attach) attaches;
 
        /* For legacy reasons, we keep all job labels that are imported in the root
         * job manager's label hash. If a job manager is an XPC domain, then it gets
@@ -390,6 +416,7 @@ struct jobmgr_s {
        mach_port_t req_bsport;
        mach_port_t req_excport;
        mach_port_t req_asport;
+       mach_port_t req_gui_asport;
        pid_t req_pid;
        uid_t req_euid;
        gid_t req_egid;
@@ -397,6 +424,7 @@ struct jobmgr_s {
        vm_offset_t req_ctx;
        mach_msg_type_number_t req_ctx_sz;
        mach_port_t req_rport;
+       uint64_t req_uniqueid;
        kern_return_t error;
        union {
                const char name[0];
@@ -409,8 +437,8 @@ static jobmgr_t _s_xpc_system_domain;
 static LIST_HEAD(, jobmgr_s) _s_xpc_user_domains;
 static LIST_HEAD(, jobmgr_s) _s_xpc_session_domains;
 
-#define jobmgr_assumes(jm, e) osx_assumes_ctx(jobmgr_log_bug, jm, (e))
-#define jobmgr_assumes_zero(jm, e) osx_assumes_zero_ctx(jobmgr_log_bug, jm, (e))
+#define jobmgr_assumes(jm, e) os_assumes_ctx(jobmgr_log_bug, jm, (e))
+#define jobmgr_assumes_zero(jm, e) os_assumes_zero_ctx(jobmgr_log_bug, jm, (e))
 #define jobmgr_assumes_zero_p(jm, e) posix_assumes_zero_ctx(jobmgr_log_bug, jm, (e))
 
 static jobmgr_t jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag, const char *name, bool no_init, mach_port_t asport);
@@ -429,6 +457,7 @@ static void jobmgr_dispatch_all(jobmgr_t jm, bool newmounthack);
 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, bool anon_okay);
 static job_t jobmgr_find_by_pid(jobmgr_t jm, pid_t p, bool create_anon);
+static job_t managed_job(pid_t p);
 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, mach_port_t *mp);
@@ -439,9 +468,9 @@ static void jobmgr_export_env_from_other_jobs(jobmgr_t jm, launch_data_t dict);
 static struct machservice *jobmgr_lookup_service(jobmgr_t jm, const char *name, bool check_parent, pid_t target_pid);
 static void jobmgr_logv(jobmgr_t jm, int pri, int err, const char *msg, va_list ap) __attribute__((format(printf, 4, 0)));
 static void jobmgr_log(jobmgr_t jm, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
-static void jobmgr_log_perf_statistics(jobmgr_t jm);
+static void jobmgr_log_perf_statistics(jobmgr_t jm, bool signal_children);
 // static void jobmgr_log_error(jobmgr_t jm, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
-static bool jobmgr_log_bug(aslmsg asl_message, void *ctx, const char *message);
+static bool jobmgr_log_bug(_SIMPLE_STRING asl_message, void *ctx, const char *message);
 
 #define AUTO_PICK_LEGACY_LABEL (const char *)(~0)
 #define AUTO_PICK_ANONYMOUS_LABEL (const char *)(~1)
@@ -460,6 +489,7 @@ struct job_s {
        LIST_ENTRY(job_s) needing_session_sle;
        LIST_ENTRY(job_s) jetsam_sle;
        LIST_ENTRY(job_s) pid_hash_sle;
+       LIST_ENTRY(job_s) global_pid_hash_sle;
        LIST_ENTRY(job_s) label_hash_sle;
        LIST_ENTRY(job_s) global_env_sle;
        SLIST_ENTRY(job_s) curious_jobs_sle;
@@ -475,8 +505,9 @@ struct job_s {
        SLIST_HEAD(, machservice) machservices;
        SLIST_HEAD(, semaphoreitem) semaphores;
        SLIST_HEAD(, waiting_for_removal) removal_watchers;
+       struct waiting4attach *w4a;
+       job_t original;
        job_t alias;
-       struct rusage ru;
        cpu_type_t *j_binpref;
        size_t j_binpref_cnt;
        mach_port_t j_port;
@@ -496,22 +527,26 @@ struct job_s {
        char *stdoutpath;
        char *stderrpath;
        char *alt_exc_handler;
+       char *cfbundleidentifier;
        unsigned int nruns;
        uint64_t trt;
 #if HAVE_SANDBOX
        char *seatbelt_profile;
        uint64_t seatbelt_flags;
+       char *container_identifier;
 #endif
 #if HAVE_QUARANTINE
        void *quarantine_data;
        size_t quarantine_data_sz;
 #endif
        pid_t p;
+       uint64_t uniqueid;
        int last_exit_status;
        int stdin_fd;
        int fork_fd;
        int nice;
        uint32_t pstype;
+       uint32_t psproctype;
        int32_t jetsam_priority;
        int32_t jetsam_memlimit;
        int32_t main_thread_priority;
@@ -520,13 +555,12 @@ struct job_s {
        uint64_t sent_signal_time;
        uint64_t start_time;
        uint32_t min_run_time;
+       bool unthrottle;
        uint32_t start_interval;
        uint32_t peruser_suspend_count;
        uuid_t instance_id;
        mode_t mask;
-       pid_t tracing_pid;
        mach_port_t asport;
-       // Only set for per-user launchd's.
        au_asid_t asid;
        uuid_t expected_audit_uuid;
        bool    
@@ -629,12 +663,6 @@ struct job_s {
                reaped:1,
                // job_stop() was called.
                stopped:1,
-               // The job is considered "frontmost" by Jetsam.
-               jetsam_frontmost:1,
-               /* The job is not frontmost, but it is considered "active" (i.e.
-                * backgrounded) by Jetsam.
-                */
-               jetsam_active:1,
                /* The job is to be kept alive continuously, but it must first get an
                 * initial kick off.
                 */
@@ -647,6 +675,8 @@ struct job_s {
                 * of the root user. This is SpringBoard.
                 */
                embedded_god:1,
+               // The job is responsible for drawing the home screen on embedded.
+               embedded_home:1,
                // We got NOTE_EXEC for the job.
                did_exec:1,
                // The job is an XPC service, and XPC proxy successfully exec(3)ed.
@@ -655,6 +685,8 @@ struct job_s {
                holds_ref:1,
                // The job has Jetsam limits in place.
                jetsam_properties:1,
+               // The job's Jetsam memory limits should only be applied in the background
+               jetsam_memory_limit_background:1,
                /* This job was created as the result of a look up of a service provided
                 * by a MultipleInstance job.
                 */
@@ -690,13 +722,23 @@ struct job_s {
                // The job is an app (on either iOS or OS X) and has different resource
                // limitations.
                app:1,
+               // FairPlay decryption failed on the job. This should only ever happen
+               // to apps.
+               fpfail:1,
                // The job failed to exec(3) for reasons that may be transient, so we're
                // waiting for UserEventAgent to tell us when it's okay to try spawning
                // again (i.e. when the executable path appears, when the UID appears,
                // etc.).
                waiting4ok:1,
+               // The job exited due to memory pressure.
+               jettisoned:1,
+               // The job supports idle-exit.
+               idle_exit:1,
                // The job was implicitly reaped by the kernel.
-               implicit_reap:1;
+               implicit_reap:1,
+               system_app :1,
+               joins_gui_session :1,
+               low_priority_background_io :1;
 
        const char label[0];
 };
@@ -704,9 +746,10 @@ struct job_s {
 static size_t hash_label(const char *label) __attribute__((pure));
 static size_t hash_ms(const char *msstr) __attribute__((pure));
 static SLIST_HEAD(, job_s) s_curious_jobs;
+static LIST_HEAD(, job_s) managed_actives[ACTIVE_JOB_HASH_SIZE];
 
-#define job_assumes(j, e) osx_assumes_ctx(job_log_bug, j, (e))
-#define job_assumes_zero(j, e) osx_assumes_zero_ctx(job_log_bug, j, (e))
+#define job_assumes(j, e) os_assumes_ctx(job_log_bug, j, (e))
+#define job_assumes_zero(j, e) os_assumes_zero_ctx(job_log_bug, j, (e))
 #define job_assumes_zero_p(j, e) posix_assumes_zero_ctx(job_log_bug, j, (e))
 
 static void job_import_keys(launch_data_t obj, const char *key, void *context);
@@ -749,8 +792,11 @@ static void job_kill(job_t j);
 static void job_uncork_fork(job_t j);
 static void job_logv(job_t j, int pri, int err, const char *msg, va_list ap) __attribute__((format(printf, 4, 0)));
 static void job_log_error(job_t j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
-static bool job_log_bug(aslmsg asl_message, void *ctx, const char *message);
-static void job_log_perf_statistics(job_t j);
+static bool job_log_bug(_SIMPLE_STRING asl_message, void *ctx, const char *message);
+static void job_log_perf_statistics(job_t j, struct rusage_info_v1 *ri, int64_t exit_status);
+#if HAVE_SYSTEMSTATS
+static void job_log_systemstats(pid_t pid, uint64_t uniqueid, uint64_t parent_uniqueid, pid_t req_pid, uint64_t req_uniqueid, const char *name, struct rusage_info_v1 *ri, int64_t exit_status);
+#endif
 static void job_set_exception_port(job_t j, mach_port_t port);
 static kern_return_t job_mig_spawn_internal(job_t j, vm_offset_t indata, mach_msg_type_number_t indataCnt, mach_port_t asport, job_t *outj);
 static void job_open_shutdown_transaction(job_t ji);
@@ -758,20 +804,44 @@ static void job_close_shutdown_transaction(job_t ji);
 static launch_data_t job_do_legacy_ipc_request(job_t j, launch_data_t request, mach_port_t asport);
 static void job_setup_per_user_directory(job_t j, uid_t uid, const char *path);
 static void job_setup_per_user_directories(job_t j, uid_t uid, const char *label);
+static void job_update_jetsam_properties(job_t j, xpc_jetsam_band_t band, uint64_t user_data);
+static void job_update_jetsam_memory_limit(job_t j, int32_t limit);
+
+#if TARGET_OS_EMBEDDED
+static bool job_import_defaults(launch_data_t pload);
+#endif
+
+static struct priority_properties_t {
+       long long band;
+       int priority;
+} _launchd_priority_map[] = {
+       { XPC_JETSAM_BAND_SUSPENDED, JETSAM_PRIORITY_IDLE },
+       { XPC_JETSAM_BAND_BACKGROUND_OPPORTUNISTIC, JETSAM_PRIORITY_BACKGROUND_OPPORTUNISTIC },
+       { XPC_JETSAM_BAND_BACKGROUND, JETSAM_PRIORITY_BACKGROUND },
+       { XPC_JETSAM_BAND_MAIL, JETSAM_PRIORITY_MAIL },
+       { XPC_JETSAM_BAND_PHONE, JETSAM_PRIORITY_PHONE },
+       { XPC_JETSAM_BAND_UI_SUPPORT, JETSAM_PRIORITY_UI_SUPPORT },
+       { XPC_JETSAM_BAND_FOREGROUND_SUPPORT, JETSAM_PRIORITY_FOREGROUND_SUPPORT },
+       { XPC_JETSAM_BAND_FOREGROUND, JETSAM_PRIORITY_FOREGROUND },
+       { XPC_JETSAM_BAND_AUDIO, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY },
+       { XPC_JETSAM_BAND_ACCESSORY, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY },
+       { XPC_JETSAM_BAND_CRITICAL, JETSAM_PRIORITY_CRITICAL },
+       { XPC_JETSAM_BAND_TELEPHONY, JETSAM_PRIORITY_TELEPHONY },
+};
 
 static const struct {
        const char *key;
        int val;
 } launchd_keys2limits[] = {
-       { LAUNCH_JOBKEY_RESOURCELIMIT_CORE,     RLIMIT_CORE     },
-       { LAUNCH_JOBKEY_RESOURCELIMIT_CPU,      RLIMIT_CPU      },
-       { LAUNCH_JOBKEY_RESOURCELIMIT_DATA,     RLIMIT_DATA     },
-       { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE,    RLIMIT_FSIZE    },
-       { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK,  RLIMIT_MEMLOCK  },
-       { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE,   RLIMIT_NOFILE   },
-       { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC,    RLIMIT_NPROC    },
-       { LAUNCH_JOBKEY_RESOURCELIMIT_RSS,      RLIMIT_RSS      },
-       { LAUNCH_JOBKEY_RESOURCELIMIT_STACK,    RLIMIT_STACK    },
+       { LAUNCH_JOBKEY_RESOURCELIMIT_CORE, RLIMIT_CORE },
+       { LAUNCH_JOBKEY_RESOURCELIMIT_CPU, RLIMIT_CPU },
+       { LAUNCH_JOBKEY_RESOURCELIMIT_DATA, RLIMIT_DATA },
+       { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE, RLIMIT_FSIZE },
+       { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK, RLIMIT_MEMLOCK },
+       { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE, RLIMIT_NOFILE },
+       { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC, RLIMIT_NPROC },
+       { LAUNCH_JOBKEY_RESOURCELIMIT_RSS, RLIMIT_RSS },
+       { LAUNCH_JOBKEY_RESOURCELIMIT_STACK, RLIMIT_STACK },
 };
 
 static time_t cronemu(int mon, int mday, int hour, int min);
@@ -802,8 +872,13 @@ static int xpc_event_channel_look_up(job_t j, xpc_object_t request, xpc_object_t
 static int xpc_event_provider_check_in(job_t j, xpc_object_t request, xpc_object_t *reply);
 static int xpc_event_provider_set_state(job_t j, xpc_object_t request, xpc_object_t *reply);
 
+#pragma mark XPC Process Forward Declarations
+static int xpc_process_set_jetsam_band(job_t j, xpc_object_t request, xpc_object_t *reply);
+static int xpc_process_set_jetsam_memory_limit(job_t j, xpc_object_t request, xpc_object_t *reply);
+
 // file local globals
 static job_t _launchd_embedded_god = NULL;
+static job_t _launchd_embedded_home = NULL;
 static size_t total_children;
 static size_t total_anon_children;
 static mach_port_t the_exception_server;
@@ -815,11 +890,14 @@ static job_t _launchd_event_monitor;
 static job_t _launchd_xpc_bootstrapper;
 static job_t _launchd_shutdown_monitor;
 
+#if TARGET_OS_EMBEDDED
+static xpc_object_t _launchd_defaults_cache;
+
+mach_port_t launchd_audit_port = MACH_PORT_DEAD;
+pid_t launchd_audit_session = 0;
+#else
 mach_port_t launchd_audit_port = MACH_PORT_NULL;
-#if !TARGET_OS_EMBEDDED
 au_asid_t launchd_audit_session = AU_DEFAUDITSID;
-#else
-pid_t launchd_audit_session = 0;
 #endif
 
 static int s_no_hang_fd = -1;
@@ -975,9 +1053,15 @@ job_export(job_t j)
        if ((tmp = launch_data_new_bool(j->ondemand))) {
                launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_ONDEMAND);
        }
-       if ((tmp = launch_data_new_integer(j->last_exit_status))) {
+
+       long long status = j->last_exit_status;
+       if (j->fpfail) {
+               status = LAUNCH_EXITSTATUS_FAIRPLAY_FAIL;
+       }
+       if ((tmp = launch_data_new_integer(status))) {
                launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_LASTEXITSTATUS);
-       } 
+       }
+
        if (j->p && (tmp = launch_data_new_integer(j->p))) {
                launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_PID);
        }
@@ -1192,9 +1276,15 @@ jobmgr_remove(jobmgr_t jm)
                        job_log(ji, LOG_ERR, "Job is still active at job manager teardown.");
                        ji->p = 0;
                }
+
                job_remove(ji);
        }
 
+       struct waiting4attach *w4ai = NULL;
+       while ((w4ai = LIST_FIRST(&jm->attaches))) {
+               waiting4attach_delete(jm, w4ai);
+       }
+
        if (jm->req_port) {
                (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(jm->req_port));
        }
@@ -1244,6 +1334,13 @@ jobmgr_remove(jobmgr_t jm)
        if (jm->parentmgr) {
                runtime_del_weak_ref();
                SLIST_REMOVE(&jm->parentmgr->submgrs, jm, jobmgr_s, sle);
+
+               // Hack for the guest user so that its stuff doesn't persist.
+               //
+               // <rdar://problem/14527875>
+               if (strcmp(jm->name, VPROCMGR_SESSION_AQUA) == 0 && getuid() == 201) {
+                       raise(SIGTERM);
+               }
        } else if (pid1_magic) {
                eliminate_double_reboot();
                launchd_log_vm_stats();
@@ -1419,10 +1516,16 @@ job_remove(job_t j)
        if (j->alt_exc_handler) {
                free(j->alt_exc_handler);
        }
+       if (j->cfbundleidentifier) {
+               free(j->cfbundleidentifier);
+       }
 #if HAVE_SANDBOX
        if (j->seatbelt_profile) {
                free(j->seatbelt_profile);
        }
+       if (j->container_identifier) {
+               free(j->container_identifier);
+       }
 #endif
 #if HAVE_QUARANTINE
        if (j->quarantine_data) {
@@ -1451,6 +1554,9 @@ job_remove(job_t j)
        if (j->embedded_god) {
                _launchd_embedded_god = NULL;
        }
+       if (j->embedded_home) {
+               _launchd_embedded_home = NULL;
+       }
        if (j->shutdown_monitor) {
                _launchd_shutdown_monitor = NULL;
        }
@@ -1714,7 +1820,7 @@ job_new_anonymous(jobmgr_t jm, pid_t anonpid)
 
                job_log(jr, LOG_DEBUG, "Created PID %u anonymously by PPID %u%s%s", anonpid, proc.pbsi_ppid, jp ? ": " : "", jp ? jp->label : "");
        } else {
-               (void)osx_assumes_zero(errno);
+               (void)os_assumes_zero(errno);
        }
 
        // Undo our hack from above.
@@ -1764,6 +1870,7 @@ job_new_subjob(job_t j, uuid_t identifier)
        job_t nj = (struct job_s *)calloc(1, sizeof(struct job_s) + label_sz + 1);
        if (nj != NULL) {
                nj->kqjob_callback = job_callback;
+               nj->original = j;
                nj->mgr = j->mgr;
                nj->min_run_time = j->min_run_time;
                nj->timeout = j->timeout;
@@ -1789,6 +1896,9 @@ job_new_subjob(job_t j, uuid_t identifier)
                nj->dedicated_instance = true;
                nj->xpc_service = j->xpc_service;
                nj->xpc_bootstrapper = j->xpc_bootstrapper;
+               nj->jetsam_priority = j->jetsam_priority;
+               nj->jetsam_memlimit = j->jetsam_memlimit;
+               nj->psproctype = j->psproctype;
 
                nj->mask = j->mask;
                uuid_copy(nj->instance_id, identifier);
@@ -1829,12 +1939,15 @@ job_new_subjob(job_t j, uuid_t identifier)
                         */
                        if (msi->upfront) {
                                mach_port_t mp = MACH_PORT_NULL;
-                               struct machservice *msj = machservice_new(nj, msi->name, &mp, msi->per_pid);
+                               struct machservice *msj = machservice_new(nj, msi->name, &mp, false);
                                if (msj != NULL) {
                                        msj->reset = msi->reset;
                                        msj->delete_on_destruction = msi->delete_on_destruction;
                                        msj->drain_one_on_crash = msi->drain_one_on_crash;
                                        msj->drain_all_on_crash = msi->drain_all_on_crash;
+
+                                       kern_return_t kr = mach_port_set_attributes(mach_task_self(), msj->port, MACH_PORT_TEMPOWNER, NULL, 0);
+                                       (void)job_assumes_zero(j, kr);
                                } else {
                                        (void)job_assumes_zero(nj, errno);
                                }
@@ -1883,10 +1996,16 @@ job_new_subjob(job_t j, uuid_t identifier)
                if (j->alt_exc_handler) {
                        nj->alt_exc_handler = strdup(j->alt_exc_handler);
                }
+               if (j->cfbundleidentifier) {
+                       nj->cfbundleidentifier = strdup(j->cfbundleidentifier);
+               }
 #if HAVE_SANDBOX
                if (j->seatbelt_profile) {
                        nj->seatbelt_profile = strdup(j->seatbelt_profile);
                }
+               if (j->container_identifier) {
+                       nj->container_identifier = strdup(j->container_identifier);
+               }
 #endif
 
 #if HAVE_QUARANTINE
@@ -1919,7 +2038,7 @@ job_new_subjob(job_t j, uuid_t identifier)
                LIST_INSERT_HEAD(&where2put->label_hash[hash_label(nj->label)], nj, label_hash_sle);
                LIST_INSERT_HEAD(&j->subjobs, nj, subjob_sle);
        } else {
-               (void)osx_assumes_zero(errno);
+               (void)os_assumes_zero(errno);
        }
 
        return nj;
@@ -1937,7 +2056,7 @@ job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *arg
        size_t i, cc = 0;
        job_t j;
 
-       __OSX_COMPILETIME_ASSERT__(offsetof(struct job_s, kqjob_callback) == 0);
+       __OS_COMPILETIME_ASSERT__(offsetof(struct job_s, kqjob_callback) == 0);
 
        if (unlikely(jm->shutting_down)) {
                errno = EINVAL;
@@ -1981,7 +2100,7 @@ job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *arg
        j = calloc(1, sizeof(struct job_s) + minlabel_len + 1);
 
        if (!j) {
-               (void)osx_assumes_zero(errno);
+               (void)os_assumes_zero(errno);
                return NULL;
        }
 
@@ -2008,19 +2127,28 @@ job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *arg
         * this priority band that its usefulness is highly questionable.
         * 
         * See <rdar://problem/9539873>.
+        *
+        * Also ensure that daemons have a default memory highwatermark unless
+        * otherwise specified, as per <rdar://problem/10307814>.
         */
        if (launchd_embedded_handofgod) {
-               j->pstype = POSIX_SPAWN_IOS_INTERACTIVE;
+               j->psproctype = POSIX_SPAWN_PROC_TYPE_APP_DEFAULT;
                j->app = true;
        } else {
-               j->pstype = POSIX_SPAWN_IOS_APPLE_DAEMON_START;
+               j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_BACKGROUND;
+               j->jetsam_memlimit = DEFAULT_JETSAM_DAEMON_HIGHWATERMARK;
        }
+#else
+       /* Jobs on OS X that just come from disk are "standard" by default so that
+        * third-party daemons/agents don't encounter unexpected throttling.
+        */
+       j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_STANDARD;
 #endif
 
        if (prog) {
                j->prog = strdup(prog);
                if (!j->prog) {
-                       (void)osx_assumes_zero(errno);
+                       (void)os_assumes_zero(errno);
                        goto out_bad;
                }
        }
@@ -2087,7 +2215,7 @@ job_new_alias(jobmgr_t jm, job_t src)
 
        job_t j = calloc(1, sizeof(struct job_s) + strlen(src->label) + 1);
        if (!j) {
-               (void)osx_assumes_zero(errno);
+               (void)os_assumes_zero(errno);
                return NULL;
        }
 
@@ -2122,6 +2250,14 @@ job_new_alias(jobmgr_t jm, job_t src)
 job_t 
 job_import(launch_data_t pload)
 {
+#if TARGET_OS_EMBEDDED
+       /* If this is the special payload of default values, handle it here */
+       if (unlikely(launch_data_dict_lookup(pload, LAUNCH_JOBKEY_DEFAULTS))) {
+               job_import_defaults(pload);
+               return NULL;
+       }
+#endif    
+    
        job_t j = jobmgr_import2(root_jobmgr, pload);
 
        if (unlikely(j == NULL)) {
@@ -2137,6 +2273,82 @@ job_import(launch_data_t pload)
        return job_dispatch(j, false);
 }
 
+#if TARGET_OS_EMBEDDED
+
+bool
+job_import_defaults(launch_data_t pload)
+{
+       bool result = false;
+       xpc_object_t xd = NULL, defaults;
+   
+       if (_launchd_defaults_cache) {
+               xpc_release(_launchd_defaults_cache);
+               _launchd_defaults_cache = NULL;
+       }
+
+       xd = ld2xpc(pload);
+       if (!xd || xpc_get_type(xd) != XPC_TYPE_DICTIONARY) {
+               goto out;
+       }
+
+       defaults = xpc_dictionary_get_value(xd, LAUNCHD_JOB_DEFAULTS);
+       if (!defaults || xpc_get_type(defaults) != XPC_TYPE_DICTIONARY) {
+               goto out;
+       }
+
+       _launchd_defaults_cache = xpc_copy(defaults);
+       result = true;
+out:
+       if (xd) {
+               xpc_release(xd);
+       }
+
+       return result;
+}
+
+bool
+job_apply_defaults(job_t j) {
+       const char *test_prefix = "com.apple.test.";
+    
+       char *sb_prefix_end, *sb_suffix_start;
+       char true_job_label[strlen(j->label)];
+       const char *label;
+
+       if (((sb_prefix_end = strchr(j->label, ':')) != NULL) &&
+           ((sb_suffix_start = strchr(sb_prefix_end + 1, '[')) != NULL)) {
+               /*
+                * Workaround 'UIKitApplication:com.apple.foo[bar]' convention for the processes
+                * we're interested in. To be removed when <rdar://problem/13066361> is addressed.
+                */
+               snprintf(true_job_label, sb_suffix_start - sb_prefix_end, "%s", sb_prefix_end + 1);
+               label = true_job_label;
+       } else {
+               /* Just test the standard label */
+               label = j->label;
+       }
+    
+       /* Test for cache presence and apply if found */
+       if (_launchd_defaults_cache) { 
+               xpc_object_t props = xpc_dictionary_get_value(_launchd_defaults_cache, label);
+               if (props && xpc_get_type(props) == XPC_TYPE_DICTIONARY) {
+                       launch_data_t lv = xpc2ld(props);
+                       launch_data_dict_iterate(lv, job_import_keys, j);
+                       launch_data_free(lv);
+                       return true;
+               }
+       }
+    
+       /* Limit free? Disable the memory limit if this is a test job; see <rdar://problem/13180697> */
+       if (!strncmp(label, test_prefix, strlen(test_prefix))) {
+               j->jetsam_memlimit = -1;
+               return true;
+       }
+    
+       return false;
+}
+
+#endif
+
 launch_data_t
 job_import_bulk(launch_data_t pload)
 {
@@ -2183,6 +2395,13 @@ job_import_bool(job_t j, const char *key, bool value)
                        found_key = true;
                }
                break;
+       case 'j':
+       case 'J':
+               if (strcasecmp(key, LAUNCH_JOBKEY_JOINGUISESSION) == 0) {
+                       j->joins_gui_session = value;
+                       found_key = true;
+               }
+               break;
        case 'k':
        case 'K':
                if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE) == 0) {
@@ -2247,6 +2466,9 @@ job_import_bool(job_t j, const char *key, bool value)
                } else if (strcasecmp(key, LAUNCH_JOBKEY_LAUNCHONLYONCE) == 0) {
                        j->only_once = value;
                        found_key = true;
+               } else if (strcasecmp(key, LAUNCH_JOBKEY_LOWPRIORITYBACKGROUNDIO) == 0) {
+                       j->low_priority_background_io = true;
+                       found_key = true;
                }
                break;
        case 'm':
@@ -2307,6 +2529,18 @@ job_import_bool(job_t j, const char *key, bool value)
                        job_log(j, LOG_ERR, "This key is not supported on this platform: %s", key);
 #endif
                        found_key = true;
+               } else if (strcasecmp(key, LAUNCH_JOBKEY_EMBEDDEDHOMESCREEN) == 0) {
+#if TARGET_OS_EMBEDDED
+                       if (!_launchd_embedded_home) {
+                               if ((j->embedded_home = value)) {
+                                       _launchd_embedded_home = j;
+                               }
+                       } else {
+                               job_log(j, LOG_ERR, "Job tried to claim %s after it has already been claimed.", key);
+                       }
+#else
+                       job_log(j, LOG_ERR, "This key is not supported on this platform: %s", key);
+#endif
                } else if (strcasecmp(key, LAUNCH_JOBKEY_EVENTMONITOR) == 0) {
                        if (!_launchd_event_monitor) {
                                j->event_monitor = value;
@@ -2357,6 +2591,12 @@ job_import_string(job_t j, const char *key, const char *value)
        char **where2put = NULL;
 
        switch (key[0]) {
+       case 'c':
+       case 'C':
+               if (strcasecmp(key, LAUNCH_JOBKEY_CFBUNDLEIDENTIFIER) == 0) {
+                       where2put = &j->cfbundleidentifier;
+               }
+               break;
        case 'm':
        case 'M':
                if (strcasecmp(key, LAUNCH_JOBKEY_MACHEXCEPTIONHANDLER) == 0) {
@@ -2367,29 +2607,24 @@ job_import_string(job_t j, const char *key, const char *value)
        case 'P':
                if (strcasecmp(key, LAUNCH_JOBKEY_PROGRAM) == 0) {
                        return;
-               } else if (strcasecmp(key, LAUNCH_JOBKEY_POSIXSPAWNTYPE) == 0) {
-                       if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_TALAPP) == 0) {
-#if !TARGET_OS_EMBEDDED
-                               j->pstype = POSIX_SPAWN_OSX_TALAPP_START;
-#endif
-                       } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_WIDGET) == 0) {
-#if !TARGET_OS_EMBEDDED
-                               j->pstype = POSIX_SPAWN_OSX_DBCLIENT_START;
-#endif
-                       } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_IOSAPP) == 0) {
-#if TARGET_OS_EMBEDDED
-                               j->pstype = POSIX_SPAWN_IOS_APP_START;
-#endif
-                       } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_INTERACTIVE) == 0) {
-#if TARGET_OS_EMBEDDED
-                               j->pstype = POSIX_SPAWN_IOS_INTERACTIVE;
-#endif
+               } else if (strcasecmp(key, LAUNCH_JOBKEY_POSIXSPAWNTYPE) == 0
+                               || strcasecmp(key, LAUNCH_JOBKEY_PROCESSTYPE) == 0) {
+                       if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_INTERACTIVE) == 0) {
+                               j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_INTERACTIVE;
+                       } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_ADAPTIVE) == 0) {
+                               j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_ADAPTIVE;
+                       } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_STANDARD) == 0) {
+                               j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_STANDARD;
                        } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_BACKGROUND) == 0) {
-#if TARGET_OS_EMBEDDED
-                               j->pstype = POSIX_SPAWN_IOS_APPLE_DAEMON_START;
-#endif
-                       } else if (strcasecmp(value, "Adaptive") == 0) {
-                               // Hack.
+                               j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_BACKGROUND;
+                       } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_TALAPP) == 0) {
+                               j->psproctype = POSIX_SPAWN_PROC_TYPE_APP_TAL;
+                       } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_SYSTEMAPP) == 0) {
+                               j->psproctype = POSIX_SPAWN_PROC_TYPE_APP_DEFAULT;
+                               j->system_app = true;
+                       } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_APP) == 0) {
+                               j->psproctype = POSIX_SPAWN_PROC_TYPE_APP_DEFAULT;
+                               j->app = true;
                        } else {
                                job_log(j, LOG_ERR, "Unknown value for key %s: %s", key, value);
                        }
@@ -2468,6 +2703,8 @@ job_import_string(job_t j, const char *key, const char *value)
 #if HAVE_SANDBOX
                } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXPROFILE) == 0) {
                        where2put = &j->seatbelt_profile;
+               } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXCONTAINER) == 0) {
+                       where2put = &j->container_identifier;
 #endif
                }
                break;
@@ -2501,6 +2738,17 @@ void
 job_import_integer(job_t j, const char *key, long long value)
 {
        switch (key[0]) {
+       case 'a':
+       case 'A':
+#if TARGET_OS_EMBEDDED
+               if (strcasecmp(key, LAUNCH_JOBKEY_ASID) == 0) {
+                       if (launchd_embedded_handofgod) {
+                               if (audit_session_port((au_asid_t)value, &j->asport) == -1 && errno != ENOSYS) {
+                                       (void)job_assumes_zero(j, errno);
+                               }
+                       }
+               }
+#endif
        case 'e':
        case 'E':
                if (strcasecmp(key, LAUNCH_JOBKEY_EXITTIMEOUT) == 0) {
@@ -2979,6 +3227,9 @@ jobmgr_import2(jobmgr_t jm, launch_data_t pload)
        jobmgr_log(jm, LOG_DEBUG, "Importing %s.", label);
 
        if (likely(j = job_new(jm, label, prog, argv))) {
+#if TARGET_OS_EMBEDDED
+               job_apply_defaults(j);
+#endif
                launch_data_dict_iterate(pload, job_import_keys, j);
                if (!uuid_is_null(j->expected_audit_uuid)) {
                        uuid_string_t uuid_str;
@@ -3006,12 +3257,12 @@ jobmgr_import2(jobmgr_t jm, launch_data_t pload)
                }
 
 #if TARGET_OS_EMBEDDED
-               /* SpringBoard runs at Interactive priority.
+               /* SpringBoard and backboardd must run at elevated priority.
                 *
-                * See <rdar://problem/9539873>.
+                * See <rdar://problem/9539873> and <rdar://problem/10984383>.
                 */
-               if (j->embedded_god) {
-                       j->pstype = POSIX_SPAWN_IOS_INTERACTIVE;
+               if (j->embedded_god || j->embedded_home) {
+                       j->psproctype = POSIX_SPAWN_PROC_TYPE_APP_DEFAULT;
                }
 #endif
        }
@@ -3104,6 +3355,20 @@ jobmgr_find_by_pid(jobmgr_t jm, pid_t p, bool create_anon)
        return create_anon ? job_new_anonymous(jm, p) : NULL;
 }
 
+job_t
+managed_job(pid_t p)
+{
+       job_t ji;
+
+       LIST_FOREACH(ji, &managed_actives[ACTIVE_JOB_HASH(p)], pid_hash_sle) {
+               if (ji->p == p) {
+                       return ji;
+               }
+       }
+
+       return NULL;
+}
+
 job_t 
 job_mig_intran2(jobmgr_t jm, mach_port_t mport, pid_t upid)
 {
@@ -3211,7 +3476,7 @@ job_export_all(void)
        if (resp != NULL) {
                job_export_all2(root_jobmgr, resp);
        } else {
-               (void)osx_assumes_zero(errno);
+               (void)os_assumes_zero(errno);
        }
 
        return resp;
@@ -3264,10 +3529,38 @@ out:
        free(pids);
 }
 
+#if HAVE_SYSTEMSTATS
+static void
+systemstats_timer_callback(void)
+{
+       jobmgr_log_perf_statistics(root_jobmgr, true);
+}
+
+static bool
+systemstats_is_enabled(void)
+{
+       static bool systemstats_enabled;
+
+       if (!systemstats_enabled) {
+               char *store = launchd_copy_persistent_store(LAUNCHD_PERSISTENT_STORE_LOGS, NULL);
+               systemstats_enabled = systemstats_init(SYSTEMSTATS_WRITER_launchd, store);
+               free(store);
+
+               uint64_t interval;
+               interval = systemstats_get_log_interval(SYSTEMSTATS_WRITER_launchd);
+
+               if (pid1_magic && systemstats_enabled && interval) {
+                       jobmgr_assumes_zero_p(root_jobmgr, kevent_mod((uintptr_t)systemstats_timer_callback, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, interval, root_jobmgr));
+               }
+       }
+
+       return systemstats_enabled;
+}
+#endif // HAVE_SYSTEMSTATS
+
 void
 job_reap(job_t j)
 {
-       struct rusage ru;
        bool is_system_bootstrapper = ((j->is_bootstrapper && pid1_magic) && !j->mgr->parentmgr);
 
        job_log(j, LOG_DEBUG, "Reaping");
@@ -3282,9 +3575,39 @@ job_reap(job_t j)
                j->fork_fd = 0;
        }
 
+       bool was_dirty = false;
+       if (!(j->anonymous || j->implicit_reap)) {
+               uint32_t flags = 0;
+               (void)job_assumes_zero(j, proc_get_dirty(j->p, &flags));
+
+               j->idle_exit = (flags & PROC_DIRTY_ALLOWS_IDLE_EXIT);
+               was_dirty = (flags & PROC_DIRTY_IS_DIRTY);
+
+               job_log(j, LOG_DEBUG, "%sob exited %s.", j->idle_exit ? "Idle-exit j" : "J", was_dirty ? "while dirty" : "cleanly");
+       }
+
+       if (j->idle_exit && was_dirty) {
+               if (j->jettisoned) {
+                       job_log(j, LOG_NOTICE, "Idle-exit job was jettisoned while dirty. Will respawn immediately.");
+                       j->unthrottle = true;
+                       j->start_pending = true;
+               } else {
+                       job_log(j, LOG_INFO, "Idle-exit job exited while dirty.");
+               }
+       } else if (j->idle_exit && j->jettisoned) {
+               /* If an idle-exit job is jettisoned, then we shouldn't throttle its
+                * next respawn because it could not help when it exited. If it ran for
+                * the minimum runtime, then this doesn't really matter. If it ran for
+                * less than the minimum runtime, it will not be throttled.
+                *
+                * <rdar://problem/12098667>
+                */
+               job_log(j, LOG_NOTICE, "Idle-exit job was jettisoned. Will bypass throttle interval for next on-demand launch.");
+               j->unthrottle = true;
+       }
+
        if (j->anonymous) {
                j->last_exit_status = 0;
-               memset(&ru, 0, sizeof(ru));
        } else {
                uint64_t rt = runtime_get_nanoseconds_since(j->start_time);
                j->trt += rt;
@@ -3320,31 +3643,32 @@ job_reap(job_t j)
                        if (j->workaround9359725) {
                                job_log(j, LOG_NOTICE, "Simulated exit: <rdar://problem/9359725>");
                                j->last_exit_status = W_EXITCODE(-1, SIGSEGV);
-                               memset(&ru, 0, sizeof(ru));
-                       } else if ((r = wait4(j->p, &j->last_exit_status, 0, &ru)) == -1) {
-                               job_log(j, LOG_ERR, "Reap failed. Assuming job exited: %d: %s", errno, strerror(errno));
-                               j->last_exit_status = W_EXITCODE(-1, SIGSEGV);
-                               memset(&ru, 0, sizeof(ru));
-                       }
-               }
+                       } else {
+#if HAVE_SYSTEMSTATS
+                               int r2;
+                               struct rusage_info_v1 ri;
+                               r2 = job_assumes_zero(j, proc_pid_rusage(j->p, RUSAGE_INFO_V1, (rusage_info_t)&ri));
+#endif
+                               if ((r = wait4(j->p, &j->last_exit_status, 0, NULL)) == -1) {
+                                       job_log(j, LOG_ERR, "Reap failed. Assuming job exited: %d: %s", errno, strerror(errno));
+                                       j->last_exit_status = W_EXITCODE(-1, SIGSEGV);
+                               }
 
-               if (launchd_log_perf && r != -1) {
-                       job_log(j, LOG_PERF, "Last instance user time: %ld.%06u", ru.ru_utime.tv_sec, ru.ru_utime.tv_usec);
-                       job_log(j, LOG_PERF, "Last instance system time: %ld.%06u", ru.ru_stime.tv_sec, ru.ru_stime.tv_usec);
-                       job_log(j, LOG_PERF, "Last instance maximum resident size: %lu", ru.ru_maxrss);
-                       job_log(j, LOG_PERF, "Last instance integral shared memory size: %lu", ru.ru_ixrss);
-                       job_log(j, LOG_PERF, "Last instance integral unshared data size: %lu", ru.ru_idrss);
-                       job_log(j, LOG_PERF, "Last instance integral unshared stack size: %lu", ru.ru_isrss);
-                       job_log(j, LOG_PERF, "Last instance page reclaims: %lu", ru.ru_minflt);
-                       job_log(j, LOG_PERF, "Last instance page faults: %lu", ru.ru_majflt);
-                       job_log(j, LOG_PERF, "Last instance swaps: %lu", ru.ru_nswap);
-                       job_log(j, LOG_PERF, "Last instance input ops: %lu", ru.ru_inblock);
-                       job_log(j, LOG_PERF, "Last instance output ops: %lu", ru.ru_oublock);
-                       job_log(j, LOG_PERF, "Last instance messages sent: %lu", ru.ru_msgsnd);
-                       job_log(j, LOG_PERF, "Last instance messages received: %lu", ru.ru_msgrcv);
-                       job_log(j, LOG_PERF, "Last instance signals received: %lu", ru.ru_nsignals);
-                       job_log(j, LOG_PERF, "Last instance voluntary context switches: %lu", ru.ru_nvcsw);
-                       job_log(j, LOG_PERF, "Last instance involuntary context switches: %lu", ru.ru_nivcsw);
+                               if (j->idle_exit && j->jettisoned) {
+                                       // Treat idle-exit jettisons as successful exit.
+                                       //
+                                       // <rdar://problem/13338973>
+                                       (void)job_assumes_zero(j, WTERMSIG(j->last_exit_status));
+                                       j->last_exit_status = W_EXITCODE(0, 0);
+                               }
+#if HAVE_SYSTEMSTATS
+                               if (r2 == 0) {
+                                       job_log_perf_statistics(j, &ri, j->last_exit_status);
+                               }
+#endif
+                       }
+               } else {
+                       job_log(j, LOG_INFO, "Job was implicitly reaped by the kernel.");
                }
        }
 
@@ -3353,6 +3677,9 @@ job_reap(job_t j)
        }
 
        LIST_REMOVE(j, pid_hash_sle);
+       if (!j->anonymous) {
+               LIST_REMOVE(j, global_pid_hash_sle);
+       }
 
        if (j->sent_signal_time) {
                uint64_t td_sec, td_usec, td = runtime_get_nanoseconds_since(j->sent_signal_time);
@@ -3363,27 +3690,6 @@ job_reap(job_t j)
                job_log(j, LOG_DEBUG, "Exited %llu.%06llu seconds after the first signal was sent", td_sec, td_usec);
        }
 
-       timeradd(&ru.ru_utime, &j->ru.ru_utime, &j->ru.ru_utime);
-       timeradd(&ru.ru_stime, &j->ru.ru_stime, &j->ru.ru_stime);
-       if (j->ru.ru_maxrss < ru.ru_maxrss) {
-               j->ru.ru_maxrss = ru.ru_maxrss;
-       }
-
-       j->ru.ru_ixrss += ru.ru_ixrss;
-       j->ru.ru_idrss += ru.ru_idrss;
-       j->ru.ru_isrss += ru.ru_isrss;
-       j->ru.ru_minflt += ru.ru_minflt;
-       j->ru.ru_majflt += ru.ru_majflt;
-       j->ru.ru_nswap += ru.ru_nswap;
-       j->ru.ru_inblock += ru.ru_inblock;
-       j->ru.ru_oublock += ru.ru_oublock;
-       j->ru.ru_msgsnd += ru.ru_msgsnd;
-       j->ru.ru_msgrcv += ru.ru_msgrcv;
-       j->ru.ru_nsignals += ru.ru_nsignals;
-       j->ru.ru_nvcsw += ru.ru_nvcsw;
-       j->ru.ru_nivcsw += ru.ru_nivcsw;
-       job_log_perf_statistics(j);
-
        int exit_status = WEXITSTATUS(j->last_exit_status);
        if (WIFEXITED(j->last_exit_status) && exit_status != 0) {
                if (!j->did_exec && _launchd_support_system) {
@@ -3405,7 +3711,7 @@ job_reap(job_t j)
                                        xpc_dictionary_set_string(event, "GroupName", j->groupname);
                                }
 
-                               (void)externalevent_new(j, _launchd_support_system, j->label, event);
+                               (void)externalevent_new(j, _launchd_support_system, j->label, event, 0);
                                xpc_release(event);
 
                                j->waiting4ok = true;
@@ -3426,7 +3732,7 @@ job_reap(job_t j)
                int s = WTERMSIG(j->last_exit_status);
                if ((SIGKILL == s || SIGTERM == s) && !j->stopped) {
                        job_log(j, LOG_NOTICE, "Exited: %s", strsignal(s));
-               } else if (!j->stopped && !j->clean_kill) {                     
+               } else if (!(j->stopped || j->clean_kill || j->jettisoned)) {
                        switch (s) {
                        // Signals which indicate a crash.
                        case SIGILL:
@@ -3564,6 +3870,7 @@ job_reap(job_t j)
        j->clean_kill = false;
        j->event_monitor_ready2signal = false;
        j->p = 0;
+       j->uniqueid = 0;
 }
 
 void
@@ -3678,16 +3985,6 @@ job_dispatch(job_t j, bool kickstart)
                } else {
                        job_log(j, LOG_DEBUG, "Watching job.");
                        job_watch(j);
-
-                       /*
-                        * 5455720
-                        *
-                        * Path checking and monitoring is really racy right now.
-                        * We should clean this up post Leopard.
-                        */
-                       if (job_keepalive(j)) {
-                               job_start(j);
-                       }
                }
        } else {
                job_log(j, LOG_DEBUG, "Tried to dispatch an already active job: %s.", job_active(j));
@@ -3829,11 +4126,12 @@ job_callback_proc(job_t j, struct kevent *kev)
        }
 
        if (fflags & NOTE_EXIT) {
-               if (kev->data & NOTE_EXIT_REPARENTED) {
-                       j->implicit_reap = true;
-                       j->last_exit_status = (kev->data & 0xffff);
-
-                       job_log(j, LOG_INFO, "Job was implicitly reaped by the kernel.");
+               if (kev->data & NOTE_EXIT_DECRYPTFAIL) {
+                       j->fpfail = true;
+                       job_log(j, LOG_WARNING, "FairPlay decryption failed on binary for job.");
+               } else if (kev->data & NOTE_EXIT_MEMORY) {
+                       j->jettisoned = true;
+                       job_log(j, LOG_INFO, "Job was killed due to memory pressure.");
                }
 
                job_reap(j);
@@ -3842,6 +4140,14 @@ job_callback_proc(job_t j, struct kevent *kev)
                        job_remove(j);
                        j = NULL;
                } else {
+                       struct waiting4attach *w4ai = NULL;
+                       struct waiting4attach *w4ait = NULL;
+                       LIST_FOREACH_SAFE(w4ai, &_launchd_domain_waiters, le, w4ait) {
+                               if (w4ai->dest == (pid_t)kev->ident) {
+                                       waiting4attach_delete(j->mgr, w4ai);
+                               }
+                       }
+
                        (void)job_dispatch(j, false);
                }
        }
@@ -3874,33 +4180,30 @@ job_callback_timer(job_t j, void *ident)
                        job_log(j, LOG_WARNING | LOG_CONSOLE, "Job has not died after being %skilled %llu seconds ago. Simulating exit.", j->clean_kill ? "cleanly " : "", td);
                        j->workaround9359725 = true;
 
+                       // This basically has to be done off the main thread. We have no
+                       // mechanism for draining the main queue in our run loop (like CF
+                       // does), and the kevent mechanism wants an object to be associated
+                       // as the callback. So we just create a dispatch source and reap the
+                       // errant PID whenever we can. Note that it is not safe for us to do
+                       // any logging in this block, since logging requires exclusive
+                       // access to global data structures that is only protected by the
+                       // main thread.
+                       dispatch_source_t hack_13570156 = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, j->p, DISPATCH_PROC_EXIT, dispatch_get_global_queue(0, 0));
+                       dispatch_source_set_event_handler(hack_13570156, ^{
+                               pid_t pid = (pid_t)dispatch_source_get_handle(hack_13570156);
+
+                               int status = 0;
+                               (void)waitpid(pid, &status, 0);
+                               dispatch_release(hack_13570156);
+                       });
+
+                       dispatch_resume(hack_13570156);
+
                        if (launchd_trap_sigkill_bugs) {
                                job_log(j, LOG_NOTICE | LOG_CONSOLE, "Trapping into kernel debugger. You can continue the machine after it has been debugged, and shutdown will proceed normally.");
                                (void)job_assumes_zero(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER));
                        }
 
-                       /* We've simulated the exit, so we have to cancel the kevent for
-                        * this job, otherwise we may get a kevent later down the road that
-                        * has a stale context pointer (if we've removed the job). Or worse,
-                        * it'll corrupt our data structures if the job still exists or the
-                        * allocation was recycled.
-                        *
-                        * If the failing process had a tracer attached to it, we need to
-                        * remove out NOTE_EXIT for that tracer too, otherwise the same
-                        * thing might happen.
-                        *
-                        * Note that, if we're not shutting down, this will result in a
-                        * zombie process just hanging around forever. But if the process
-                        * didn't exit after receiving SIGKILL, odds are it would've just
-                        * stuck around forever anyway.
-                        *
-                        * See <rdar://problem/9481630>.
-                        */
-                       (void)kevent_mod((uintptr_t)j->p, EVFILT_PROC, EV_DELETE, 0, 0, NULL);
-                       if (j->tracing_pid) {
-                               (void)kevent_mod((uintptr_t)j->tracing_pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL);
-                       }
-
                        struct kevent bogus_exit;
                        EV_SET(&bogus_exit, j->p, EVFILT_PROC, 0, NOTE_EXIT, 0, 0);
                        jobmgr_callback(j->mgr, &bogus_exit);
@@ -3975,7 +4278,9 @@ jobmgr_callback(void *obj, struct kevent *kev)
                        /* Hopefully /var is available by this point. If not, uh, oh well.
                         * It's just a debugging facility.
                         */
-                       return jobmgr_log_perf_statistics(jm);
+                       return jobmgr_log_perf_statistics(jm, false);
+               case SIGINFO:
+                       return jobmgr_log_perf_statistics(jm, true);
                default:
                        jobmgr_log(jm, LOG_ERR, "Unrecognized signal: %lu: %s", kev->ident, strsignal(kev->ident));
                }
@@ -4006,6 +4311,10 @@ jobmgr_callback(void *obj, struct kevent *kev)
                        if (jobmgr_assumes_zero(jm, runtime_busy_cnt) == 0) {
                                return launchd_shutdown();
                        }
+#if HAVE_SYSTEMSTATS
+               } else if (kev->ident == (uintptr_t)systemstats_timer_callback) {
+                       systemstats_timer_callback();
+#endif
                }
                break;
        case EVFILT_VNODE:
@@ -4063,7 +4372,7 @@ job_start(job_t j)
        char nbuf[64];
        pid_t c;
        bool sipc = false;
-       u_int proc_fflags = NOTE_EXIT|NOTE_FORK|NOTE_EXEC|NOTE_EXITSTATUS;
+       u_int proc_fflags = NOTE_EXIT|NOTE_FORK|NOTE_EXEC|NOTE_EXIT_DETAIL|NOTE_EXITSTATUS;
 
        if (!job_assumes(j, j->mgr != NULL)) {
                return;
@@ -4074,6 +4383,11 @@ job_start(job_t j)
                return;
        }
 
+       if (!LIST_EMPTY(&j->mgr->attaches)) {
+               job_log(j, LOG_DEBUG, "Looking for attachments for job: %s", j->label);
+               (void)waiting4attach_find(j->mgr, j);
+       }
+
        /*
         * Some users adjust the wall-clock and then expect software to not notice.
         * Therefore, launchd must use an absolute clock instead of the wall clock
@@ -4082,9 +4396,8 @@ job_start(job_t j)
        td = runtime_get_nanoseconds_since(j->start_time);
        td /= NSEC_PER_SEC;
 
-       if (j->start_time && (td < j->min_run_time) && !j->legacy_mach_job && !j->inetcompat) {
+       if (j->start_time && (td < j->min_run_time) && !j->legacy_mach_job && !j->inetcompat && !j->unthrottle) {
                time_t respawn_delta = j->min_run_time - (uint32_t)td;
-
                /* We technically should ref-count throttled jobs to prevent idle exit,
                 * but we're not directly tracking the 'throttled' state at the moment.
                 */
@@ -4139,6 +4452,8 @@ job_start(job_t j)
                job_log(j, LOG_DEBUG, "Started as PID: %u", c);
 
                j->did_exec = false;
+               j->fpfail = false;
+               j->jettisoned = false;
                j->xpcproxy_did_exec = false;
                j->checkedin = false;
                j->start_pending = false;
@@ -4147,6 +4462,7 @@ job_start(job_t j)
                j->stopped = false;
                j->workaround9359725 = false;
                j->implicit_reap = false;
+               j->unthrottle = false;
                if (j->needs_kickoff) {
                        j->needs_kickoff = false;
 
@@ -4163,8 +4479,15 @@ job_start(job_t j)
                runtime_add_ref();
                total_children++;
                LIST_INSERT_HEAD(&j->mgr->active_jobs[ACTIVE_JOB_HASH(c)], j, pid_hash_sle);
+               LIST_INSERT_HEAD(&managed_actives[ACTIVE_JOB_HASH(c)], j, global_pid_hash_sle);
                j->p = c;
 
+               struct proc_uniqidentifierinfo info;
+               if (proc_pidinfo(c, PROC_PIDUNIQIDENTIFIERINFO, 0, &info, PROC_PIDUNIQIDENTIFIERINFO_SIZE) != 0) {
+                       // ignore errors here, kevent_mod below will catch them and clean up
+                       j->uniqueid = info.p_uniqueid;
+               }
+
                j->mgr->normal_active_cnt++;
                j->fork_fd = _fd(execspair[0]);
                (void)job_assumes_zero(j, runtime_close(execspair[1]));
@@ -4192,6 +4515,23 @@ job_start(job_t j)
                        job_watch(j);
                }
 
+#if HAVE_SYSTEMSTATS
+               if (systemstats_is_enabled()) {
+                       /* We don't really *need* to make the full rusage call -- it
+                        * will be mostly 0s and very small numbers. We only need
+                        * ri_proc_start_abstime, because that's how we disambiguiate
+                        * PIDs when they wrap around; and the UUID.
+                        * In the future we should use the 64-bit process unique ID,
+                        * so there's nothing to disambiguiate, and skip the full
+                        * rusage call here.
+                        *
+                        * Well, the future is now.
+                        */
+                       if (_systemstats_get_property(SYSTEMSTATS_API_VERSION, SYSTEMSTATS_WRITER_launchd, SYSTEMSTATS_PROPERTY_LAUNCHD_SHOULD_LOG_JOB_START)) {
+                               job_log_perf_statistics(j, NULL, -3);
+                       }
+               }
+#endif
                j->wait4debugger_oneshot = false;
                if (likely(!j->stall_before_exec)) {
                        job_uncork_fork(j);
@@ -4210,6 +4550,7 @@ job_start_child(job_t j)
        int gflags = GLOB_NOSORT|GLOB_NOCHECK|GLOB_TILDE|GLOB_DOOFFS;
        glob_t g;
        short spflags = POSIX_SPAWN_SETEXEC;
+       int psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_BACKGROUND;
        size_t binpref_out_cnt = 0;
        size_t i;
 
@@ -4217,7 +4558,23 @@ job_start_child(job_t j)
 
        job_setup_attributes(j);
 
-       if (unlikely(j->argv && j->globargv)) {
+       bool use_xpcproxy = false;
+       struct waiting4attach *w4a = waiting4attach_find(j->mgr, j);
+       if (w4a) {
+               (void)setenv(XPC_SERVICE_ENV_ATTACHED, "1", 1);
+               if (!j->xpc_service) {
+                       use_xpcproxy = true;
+               }
+       }
+
+       if (use_xpcproxy) {
+               argv = alloca(3 * sizeof(char *));
+               argv[0] = "/usr/libexec/xpcproxy";
+               argv[1] = "-debug";
+               argv[2] = NULL;
+
+               file2exec = argv[0];
+       } else if (unlikely(j->argv && j->globargv)) {
                g.gl_offs = 1;
                for (i = 0; i < j->argc; i++) {
                        if (i > 0) {
@@ -4244,12 +4601,12 @@ job_start_child(job_t j)
                argv[2] = NULL;
        }
 
-       if (likely(!j->inetcompat)) {
+       if (likely(!(j->inetcompat || use_xpcproxy))) {
                argv++;
        }
 
        if (unlikely(j->wait4debugger || j->wait4debugger_oneshot)) {
-               if (!j->legacy_LS_job) {
+               if (!j->app) {
                        job_log(j, LOG_WARNING, "Spawned and waiting for the debugger to attach before continuing...");
                }
                spflags |= POSIX_SPAWN_START_SUSPENDED;
@@ -4268,6 +4625,9 @@ job_start_child(job_t j)
                (void)job_assumes(j, binpref_out_cnt == j->j_binpref_cnt);
        }
 
+       psproctype = j->psproctype;
+       (void)job_assumes_zero(j, posix_spawnattr_setprocesstype_np(&spattr, psproctype));
+
 #if TARGET_OS_EMBEDDED
        /* Set jetsam attributes. POSIX_SPAWN_JETSAM_USE_EFFECTIVE_PRIORITY guards
         * against a race which arises if, during spawn, an initial jetsam property
@@ -4275,17 +4635,63 @@ job_start_child(job_t j)
         * ensures that the subsequent change is ignored; the explicit update should
         * be given priority.
         */
-       short flags = 0;
-       if (j->jetsam_properties) {
-               flags = POSIX_SPAWN_JETSAM_USE_EFFECTIVE_PRIORITY;
+       (void)job_assumes_zero(j, posix_spawnattr_setjetsam(&spattr,
+               POSIX_SPAWN_JETSAM_USE_EFFECTIVE_PRIORITY | (j->jetsam_memory_limit_background ? POSIX_SPAWN_JETSAM_HIWATER_BACKGROUND : 0), 
+               j->jetsam_priority, j->jetsam_memlimit));
+#endif
+
+       mach_port_array_t sports = NULL;
+       mach_msg_type_number_t sports_cnt = 0;
+       kern_return_t kr = vproc_mig_get_listener_port_rights(bootstrap_port, &sports, &sports_cnt);
+       if (kr == 0 && sports_cnt) {
+               /* For some reason, this SPI takes a count as a signed quantity. */
+               (void)posix_spawnattr_set_importancewatch_port_np(&spattr, (int)sports_cnt, sports);
+
+               /* All "count" parameters in MIG are counts of the array. So an array of
+                * mach_port_t containing 10 elements will have a count of ten, but it
+                * will occupy 40 bytes. So we must do the multiplication here to pass
+                * the correct size.
+                *
+                * Note that we do NOT release the send rights. We need them to be valid
+                * at the time they are passed to posix_spawn(2). When we exec(3) using
+                * posix_spawn(2), they'll be cleaned up anyway.
+                */
+               mig_deallocate((vm_address_t)sports, sports_cnt * sizeof(sports[0]));
+       } else if (kr != BOOTSTRAP_UNKNOWN_SERVICE) {
+               (void)job_assumes_zero(j, kr);
+       }
+
+#if TARGET_OS_EMBEDDED
+       if (!j->app || j->system_app) {
+               (void)job_assumes_zero(j, posix_spawnattr_setcpumonitor_default(&spattr));
        }
+#else
+       (void)job_assumes_zero(j, posix_spawnattr_setcpumonitor_default(&spattr));
+#endif
 
-       (void)job_assumes_zero(j, posix_spawnattr_setjetsam(&spattr, flags, j->jetsam_priority, j->jetsam_memlimit));
+#if !TARGET_OS_EMBEDDED
+       struct task_qos_policy qosinfo = {
+               .task_latency_qos_tier = LATENCY_QOS_LAUNCH_DEFAULT_TIER,
+               .task_throughput_qos_tier = THROUGHPUT_QOS_LAUNCH_DEFAULT_TIER,
+       };
+
+       kr = task_policy_set(mach_task_self(), TASK_BASE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
+       (void)job_assumes_zero_p(j, kr);
 #endif
 
-       if (!j->app) {
-               (void)job_assumes_zero(j, posix_spawnattr_setcpumonitor(&spattr, 85, 5 * 60));
+#if HAVE_RESPONSIBILITY
+       /* Specify which process is responsible for the new job.  Per-app XPC
+        * services are the responsibility of the app.  Other processes are
+        * responsible for themselves.  This decision is final and also applies
+        * to the process's children, so don't initialize responsibility when
+        * starting a per-user launchd.
+        */
+       if (j->mgr->req_pid) {
+               responsibility_init2(j->mgr->req_pid, NULL);
+       } else if (!j->per_user) {
+               responsibility_init2(getpid(), j->prog ? j->prog : j->argv[0]);
        }
+#endif
 
 #if HAVE_QUARANTINE
        if (j->quarantine_data) {
@@ -4300,6 +4706,19 @@ job_start_child(job_t j)
 #endif
 
 #if HAVE_SANDBOX
+#if TARGET_OS_EMBEDDED
+       struct sandbox_spawnattrs sbattrs;
+       if (j->seatbelt_profile || j->container_identifier) {
+               sandbox_spawnattrs_init(&sbattrs);
+               if (j->seatbelt_profile) {
+                       sandbox_spawnattrs_setprofilename(&sbattrs, j->seatbelt_profile);
+               }
+               if (j->container_identifier) {
+                       sandbox_spawnattrs_setcontainer(&sbattrs, j->container_identifier);
+               }
+               (void)job_assumes_zero(j, posix_spawnattr_setmacpolicyinfo_np(&spattr, "Sandbox", &sbattrs, sizeof(sbattrs)));
+       }
+#else
        if (j->seatbelt_profile) {
                char *seatbelt_err_buf = NULL;
 
@@ -4310,17 +4729,18 @@ job_start_child(job_t j)
                        goto out_bad;
                }
        }
+#endif
 #endif
 
        psf = j->prog ? posix_spawn : posix_spawnp;
 
-       if (likely(!j->inetcompat)) {
+       if (likely(!(j->inetcompat || use_xpcproxy))) {
                file2exec = j->prog ? j->prog : argv[0];
        }
 
        errno = psf(NULL, file2exec, NULL, &spattr, (char *const *)argv, environ);
 
-#if HAVE_SANDBOX
+#if HAVE_SANDBOX && !TARGET_OS_EMBEDDED
 out_bad:
 #endif
        _exit(errno);
@@ -4737,6 +5157,9 @@ job_setup_attributes(job_t j)
        if (unlikely(j->low_pri_io)) {
                (void)job_assumes_zero_p(j, setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE));
        }
+       if (j->low_priority_background_io) {
+               (void)job_assumes_zero_p(j, setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_DARWIN_BG, IOPOL_THROTTLE));
+       }
        if (unlikely(j->rootdir)) {
                (void)job_assumes_zero_p(j, chroot(j->rootdir));
                (void)job_assumes_zero_p(j, chdir("."));
@@ -4874,7 +5297,7 @@ calendarinterval_setalarm(job_t j, struct calendarinterval *ci)
 }
 
 bool
-jobmgr_log_bug(aslmsg asl_message __attribute__((unused)), void *ctx, const char *message)
+jobmgr_log_bug(_SIMPLE_STRING asl_message __attribute__((unused)), void *ctx, const char *message)
 {
        jobmgr_t jm = ctx;
        jobmgr_log(jm, LOG_ERR, "%s", message);
@@ -4883,7 +5306,7 @@ jobmgr_log_bug(aslmsg asl_message __attribute__((unused)), void *ctx, const char
 }
 
 bool
-job_log_bug(aslmsg asl_message __attribute__((unused)), void *ctx, const char *message)
+job_log_bug(_SIMPLE_STRING asl_message __attribute__((unused)), void *ctx, const char *message)
 {
        job_t j = ctx;
        job_log(j, LOG_ERR, "%s", message);
@@ -4891,89 +5314,166 @@ job_log_bug(aslmsg asl_message __attribute__((unused)), void *ctx, const char *m
        return true;
 }
 
+// ri: NULL = please sample j->p; non-NULL = use this sample
 void
-job_log_perf_statistics(job_t j)
+job_log_perf_statistics(job_t j, struct rusage_info_v1 *ri, int64_t exit_status)
 {
-       if (j->anonymous) {
+#if HAVE_SYSTEMSTATS
+       if (j->anonymous || !j->p) {
                return;
        }
-       if (!launchd_log_perf) {
+       if (!systemstats_is_enabled()) {
                return;
        }
-
-       job_log(j, LOG_PERF, "Job is currently %srunning.", j->p ? "" : "not ");
-       job_log(j, LOG_PERF, "Number of runs: %u", j->nruns);
-       if (j->nruns) {
-               job_log(j, LOG_PERF, "Total runtime: %06f.", (double)j->trt / (double)NSEC_PER_SEC);
-               job_log(j, LOG_PERF, "Total user time: %ld.%06u", j->ru.ru_utime.tv_sec, j->ru.ru_utime.tv_usec);
-               job_log(j, LOG_PERF, "Total system time: %ld.%06u", j->ru.ru_stime.tv_sec, j->ru.ru_stime.tv_usec);
-               job_log(j, LOG_PERF, "Largest maximum resident size: %lu", j->ru.ru_maxrss);
-               job_log(j, LOG_PERF, "Total integral shared memory size: %lu", j->ru.ru_ixrss);
-               job_log(j, LOG_PERF, "Total integral unshared data size: %lu", j->ru.ru_idrss);
-               job_log(j, LOG_PERF, "Total integral unshared stack size: %lu", j->ru.ru_isrss);
-               job_log(j, LOG_PERF, "Total page reclaims: %lu", j->ru.ru_minflt);
-               job_log(j, LOG_PERF, "Total page faults: %lu", j->ru.ru_majflt);
-               job_log(j, LOG_PERF, "Total swaps: %lu", j->ru.ru_nswap);
-               job_log(j, LOG_PERF, "Total input ops: %lu", j->ru.ru_inblock);
-               job_log(j, LOG_PERF, "Total output ops: %lu", j->ru.ru_oublock);
-               job_log(j, LOG_PERF, "Total messages sent: %lu", j->ru.ru_msgsnd);
-               job_log(j, LOG_PERF, "Total messages received: %lu", j->ru.ru_msgrcv);
-               job_log(j, LOG_PERF, "Total signals received: %lu", j->ru.ru_nsignals);
-               job_log(j, LOG_PERF, "Total voluntary context switches: %lu", j->ru.ru_nvcsw);
-               job_log(j, LOG_PERF, "Total involuntary context switches: %lu", j->ru.ru_nivcsw);
+       const char *name;
+       if (j->cfbundleidentifier) {
+               name = j->cfbundleidentifier;
+       } else {
+               name = j->label;
        }
-
-       if (j->p) {
-               uint64_t rt = runtime_get_nanoseconds_since(j->start_time);
-               job_log(j, LOG_PERF, "Current instance wall time: %06f", (double)rt / (double)NSEC_PER_SEC);
-
-               struct proc_taskinfo ti;
-               int r = proc_pidinfo(j->p, PROC_PIDTASKINFO, 1, &ti, PROC_PIDTASKINFO_SIZE);
-               if (r > 0) {
-                       job_log(j, LOG_PERF, "Current instance virtual size: %llu", ti.pti_virtual_size);
-                       job_log(j, LOG_PERF, "Current instance resident size: %llu", ti.pti_resident_size);
-                       job_log(j, LOG_PERF, "Current instance user time: %06f", (double)ti.pti_total_user / (double)NSEC_PER_SEC);
-                       job_log(j, LOG_PERF, "Current instance system time: %06f", (double)ti.pti_total_system / (double)NSEC_PER_SEC);
-                       job_log(j, LOG_PERF, "Current instance number of user threads: %llu", ti.pti_threads_user);
-                       job_log(j, LOG_PERF, "Current instance number of system threads: %llu", ti.pti_threads_system);
-                       job_log(j, LOG_PERF, "Current instance default thread policy: %d", ti.pti_policy);
-                       job_log(j, LOG_PERF, "Current instance number of page faults: %d", ti.pti_faults);
-                       job_log(j, LOG_PERF, "Current instance number of page-ins: %d", ti.pti_pageins);
-                       job_log(j, LOG_PERF, "Current instance number of COW faults: %d", ti.pti_cow_faults);
-                       job_log(j, LOG_PERF, "Current instance number of Mach syscalls: %d", ti.pti_syscalls_mach);
-                       job_log(j, LOG_PERF, "Current instance number of Unix syscalls: %d", ti.pti_syscalls_unix);
-                       job_log(j, LOG_PERF, "Current instance number of threads: %d", ti.pti_threadnum);
-                       job_log(j, LOG_PERF, "Current instance number of running threads: %d", ti.pti_numrunning);
-                       job_log(j, LOG_PERF, "Current instance task priority: %d", ti.pti_priority);
-               } else {
-                       job_log(j, LOG_PERF, "proc_pidinfo(%d): %d: %s", j->p, errno, strerror(errno));
-               }
+       int r = 0;
+       struct rusage_info_v1 ris;
+       if (ri == NULL) {
+               ri = &ris;
+               r = proc_pid_rusage(j->p, RUSAGE_INFO_V1, (rusage_info_t)ri);
+       }
+       if (r == -1) {
+               return;
        }
+       job_log_systemstats(j->p, j->uniqueid, runtime_get_uniqueid(), j->mgr->req_pid, j->mgr->req_uniqueid, name, ri, exit_status);
+#else
+#pragma unused (j, ri, exit_status)
+#endif
+}
 
-       if (!j->ondemand) {
-               job_log(j, LOG_PERF, "Job is configured to always run.");
+#if HAVE_SYSTEMSTATS
+// ri: NULL = don't write fields from ri; non-NULL = use this sample
+static
+void
+job_log_systemstats(pid_t pid, uint64_t uniqueid, uint64_t parent_uniqueid, pid_t req_pid, uint64_t req_uniqueid, const char *name, struct rusage_info_v1 *ri, int64_t exit_status)
+{
+       if (!systemstats_is_enabled()) {
+               return;
        }
 
-       struct machservice *msi = NULL;
-       SLIST_FOREACH(msi, &j->machservices, sle) {
-               if (msi->upfront) {
-                       job_log(j, LOG_PERF, "Job advertises service in plist: %s", msi->name);
-               } else if (!(msi->event_channel || msi->per_pid)) {
-                       job_log(j, LOG_PERF, "Job has dynamically registered service: %s", msi->name);
-               } else if (msi->per_pid) {
-                       job_log(j, LOG_PERF, "Job advertises per-PID service: %s", msi->name);
-               }
+       struct systemstats_process_usage_s info;
+       bzero(&info, sizeof(info));
+       info.name = name;
+       info.pid = pid;
+       info.exit_status = exit_status;
+       info.uid = getuid();
+       info.ppid = getpid();
+       info.responsible_pid = req_pid;
+
+       if (likely(ri)) {
+               info.macho_uuid = (const uint8_t *)&ri->ri_uuid;
+               info.user_time = ri->ri_user_time;
+               info.system_time = ri->ri_system_time;
+               info.pkg_idle_wkups = ri->ri_pkg_idle_wkups;
+               info.interrupt_wkups = ri->ri_interrupt_wkups;
+               info.proc_start_abstime = ri->ri_proc_start_abstime;
+               info.proc_exit_abstime = ri->ri_proc_exit_abstime;
+#if SYSTEMSTATS_API_VERSION >= 20130319
+               info.pageins = ri->ri_pageins;
+               info.wired_size = ri->ri_wired_size;
+               info.resident_size = ri->ri_resident_size;
+               info.phys_footprint = ri->ri_phys_footprint;
+               // info.purgeablesize = ???
+#endif
+#if SYSTEMSTATS_API_VERSION >= 20130328
+               info.child_user_time = ri->ri_child_user_time;
+               info.child_system_time = ri->ri_child_system_time;
+               info.child_pkg_idle_wkups = ri->ri_child_pkg_idle_wkups;
+               info.child_interrupt_wkups = ri->ri_child_interrupt_wkups;
+               info.child_pageins = ri->ri_child_pageins;
+               info.child_elapsed_abstime = ri->ri_child_elapsed_abstime;
+#endif
        }
+#if SYSTEMSTATS_API_VERSION >= 20130410
+       info.uniqueid = uniqueid;
+       info.parent_uniqueid = parent_uniqueid;
+       info.responsible_uniqueid = req_uniqueid;
+#endif
+       systemstats_write_process_usage(&info);
 }
+#endif /* HAVE_SYSTEMSTATS */
 
-void
-job_logv(job_t j, int pri, int err, const char *msg, va_list ap)
+struct waiting4attach *
+waiting4attach_new(jobmgr_t jm, const char *name, mach_port_t port, pid_t dest, xpc_service_type_t type)
 {
-       const char *label2use = j ? j->label : "com.apple.launchd.job-unknown";
-       const char *mgr2use = j ? j->mgr->name : "com.apple.launchd.jobmanager-unknown";
-       char *newmsg;
-       int oldmask = 0;
-       size_t newmsgsz;
+       size_t xtra = strlen(name) + 1;
+
+       struct waiting4attach *w4a = malloc(sizeof(*w4a) + xtra);
+       if (!w4a) {
+               return NULL;
+       }
+
+       w4a->port = port;
+       w4a->dest = dest;
+       w4a->type = type;
+       (void)strcpy(w4a->name, name);
+
+       if (dest) {
+               LIST_INSERT_HEAD(&_launchd_domain_waiters, w4a, le);
+       } else {
+               LIST_INSERT_HEAD(&jm->attaches, w4a, le);
+       }
+
+
+       (void)jobmgr_assumes_zero(jm, launchd_mport_notify_req(port, MACH_NOTIFY_DEAD_NAME));
+       return w4a;
+}
+
+void
+waiting4attach_delete(jobmgr_t jm, struct waiting4attach *w4a)
+{
+       jobmgr_log(jm, LOG_DEBUG, "Canceling dead-name notification for waiter port: 0x%x", w4a->port);
+
+       LIST_REMOVE(w4a, le);
+
+       mach_port_t previous = MACH_PORT_NULL;
+       (void)jobmgr_assumes_zero(jm, mach_port_request_notification(mach_task_self(), w4a->port, MACH_NOTIFY_DEAD_NAME, 0, MACH_PORT_NULL, MACH_MSG_TYPE_MOVE_SEND_ONCE, &previous));
+       if (previous) {
+               (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(previous));
+       }
+
+       jobmgr_assumes_zero(jm, launchd_mport_deallocate(w4a->port));
+       free(w4a);
+}
+
+struct waiting4attach *
+waiting4attach_find(jobmgr_t jm, job_t j)
+{
+       char *name2use = (char *)j->label;
+       if (j->app) {
+               struct envitem *ei = NULL;
+               SLIST_FOREACH(ei, &j->env, sle) {
+                       if (strcmp(ei->key, XPC_SERVICE_RENDEZVOUS_TOKEN) == 0) {
+                               name2use = ei->value;
+                               break;
+                       }
+               }
+       }
+
+       struct waiting4attach *w4ai = NULL;
+       LIST_FOREACH(w4ai, &jm->attaches, le) {
+               if (strcmp(name2use, w4ai->name) == 0) {
+                       job_log(j, LOG_DEBUG, "Found attachment: %s", name2use);
+                       break;
+               }
+       }
+
+       return w4ai;
+}
+
+void
+job_logv(job_t j, int pri, int err, const char *msg, va_list ap)
+{
+       const char *label2use = j ? j->label : "com.apple.launchd.job-unknown";
+       const char *mgr2use = j ? j->mgr->name : "com.apple.launchd.jobmanager-unknown";
+       char *newmsg;
+       int oldmask = 0;
+       size_t newmsgsz;
 
        struct launchd_syslog_attr attr = {
                .from_name = launchd_label,
@@ -5054,11 +5554,25 @@ jobmgr_log_error(jobmgr_t jm, int pri, const char *msg, ...)
 #endif
 
 void
-jobmgr_log_perf_statistics(jobmgr_t jm)
+jobmgr_log_perf_statistics(jobmgr_t jm, bool signal_children)
 {
+#if HAVE_SYSTEMSTATS
+       // Log information for kernel_task and pid 1 launchd.
+       if (systemstats_is_enabled() && pid1_magic && jm == root_jobmgr) {
+#if SYSTEMSTATS_API_VERSION >= 20130328
+               if (_systemstats_get_property(SYSTEMSTATS_API_VERSION, SYSTEMSTATS_WRITER_launchd, SYSTEMSTATS_PROPERTY_SHOULD_LOG_ENERGY_STATISTICS)) {
+                       systemstats_write_intel_energy_statistics(NULL);
+               }
+#else
+               systemstats_write_intel_energy_statistics(NULL);
+#endif
+               job_log_systemstats(0, 0, 0, 0, 0, "com.apple.kernel", NULL, -1);
+               job_log_systemstats(1, 1, 0, 1, 1, "com.apple.launchd", NULL, -1);
+       }
+#endif
        jobmgr_t jmi = NULL;
        SLIST_FOREACH(jmi, &jm->submgrs, sle) {
-               jobmgr_log_perf_statistics(jmi);
+               jobmgr_log_perf_statistics(jmi, signal_children);
        }
 
        if (jm->xpc_singleton) {
@@ -5073,7 +5587,11 @@ jobmgr_log_perf_statistics(jobmgr_t jm)
 
        job_t ji = NULL;
        LIST_FOREACH(ji, &jm->jobs, sle) {
-               job_log_perf_statistics(ji);
+               job_log_perf_statistics(ji, NULL, -1);
+               if (unlikely(signal_children) && unlikely(strstr(ji->label, "com.apple.launchd.peruser.") == ji->label)) {
+                       jobmgr_log(jm, LOG_PERF, "Sending SIGINFO to peruser launchd %d", ji->p);
+                       kill(ji->p, SIGINFO);
+               }
        }
 
        jobmgr_log(jm, LOG_PERF, "End of job list.");
@@ -5736,7 +6254,9 @@ void
 machservice_watch(job_t j, struct machservice *ms)
 {
        if (ms->recv) {
-               (void)job_assumes_zero(j, runtime_add_mport(ms->port, NULL));
+               if (job_assumes_zero(j, runtime_add_mport(ms->port, NULL)) == KERN_INVALID_RIGHT) {
+                       ms->recv_race_hack = true;
+               }
        }
 }
 
@@ -5925,7 +6445,7 @@ job_setup_exception_port(job_t j, task_t target_task)
 #endif
 
        if (likely(target_task)) {
-               kern_return_t kr = task_set_exception_ports(target_task, EXC_MASK_CRASH | EXC_MASK_RESOURCE, exc_port, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f);
+               kern_return_t kr = task_set_exception_ports(target_task, EXC_MASK_CRASH | EXC_MASK_GUARD | EXC_MASK_RESOURCE, exc_port, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f);
                if (kr) {
                        if (kr != MACH_SEND_INVALID_DEST) {
                                (void)job_assumes_zero(j, kr);
@@ -5935,7 +6455,7 @@ job_setup_exception_port(job_t j, task_t target_task)
                }
        } else if (pid1_magic && the_exception_server) {
                mach_port_t mhp = mach_host_self();
-               (void)job_assumes_zero(j, host_set_exception_ports(mhp, EXC_MASK_CRASH | EXC_MASK_RESOURCE, the_exception_server, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f));
+               (void)job_assumes_zero(j, host_set_exception_ports(mhp, EXC_MASK_CRASH | EXC_MASK_GUARD | EXC_MASK_RESOURCE, the_exception_server, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f));
                (void)job_assumes_zero(j, launchd_mport_deallocate(mhp));
        }
 }
@@ -6016,7 +6536,9 @@ machservice_setup_options(launch_data_t obj, const char *key, void *context)
                }
                break;
        case LAUNCH_DATA_DICTIONARY:
-               job_set_exception_port(ms->job, ms->port);
+               if (launch_data_dict_get_count(obj) == 0) {
+                       job_set_exception_port(ms->job, ms->port);
+               }
                break;
        default:
                break;
@@ -6047,6 +6569,9 @@ machservice_setup(launch_data_t obj, const char *key, void *context)
        if (launch_data_get_type(obj) == LAUNCH_DATA_DICTIONARY) {
                launch_data_dict_iterate(obj, machservice_setup_options, ms);
        }
+
+       kern_return_t kr = mach_port_set_attributes(mach_task_self(), ms->port, MACH_PORT_TEMPOWNER, NULL, 0);
+       (void)job_assumes_zero(j, kr);
 }
 
 jobmgr_t
@@ -6298,7 +6823,7 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo
        job_t bootstrapper = NULL;
        jobmgr_t jmr;
 
-       __OSX_COMPILETIME_ASSERT__(offsetof(struct jobmgr_s, kqjobmgr_callback) == 0);
+       __OS_COMPILETIME_ASSERT__(offsetof(struct jobmgr_s, kqjobmgr_callback) == 0);
 
        if (unlikely(jm && requestorport == MACH_PORT_NULL)) {
                jobmgr_log(jm, LOG_ERR, "Mach sub-bootstrap create request requires a requester port");
@@ -6355,10 +6880,10 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo
                // cut off the Libc cache, we don't want to deadlock against ourself
                inherited_bootstrap_port = bootstrap_port;
                bootstrap_port = MACH_PORT_NULL;
-               osx_assert_zero(launchd_mport_notify_req(inherited_bootstrap_port, MACH_NOTIFY_DEAD_NAME));
+               os_assert_zero(launchd_mport_notify_req(inherited_bootstrap_port, MACH_NOTIFY_DEAD_NAME));
 
                // We set this explicitly as we start each child
-               osx_assert_zero(launchd_set_bport(MACH_PORT_NULL));
+               os_assert_zero(launchd_set_bport(MACH_PORT_NULL));
        } else if (jobmgr_assumes_zero(jmr, launchd_mport_create_recv(&jmr->jm_port)) != KERN_SUCCESS) {
                goto out_bad;
        }
@@ -6371,6 +6896,7 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo
                (void)jobmgr_assumes_zero_p(jmr, kevent_mod(SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr));
                (void)jobmgr_assumes_zero_p(jmr, kevent_mod(SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr));
                (void)jobmgr_assumes_zero_p(jmr, kevent_mod(SIGUSR2, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr));
+               (void)jobmgr_assumes_zero_p(jmr, kevent_mod(SIGINFO, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr));
                (void)jobmgr_assumes_zero_p(jmr, kevent_mod(0, EVFILT_FS, EV_ADD, VQ_MOUNT|VQ_UNMOUNT|VQ_UPDATE, 0, jmr));
        }
 
@@ -6523,7 +7049,7 @@ jobmgr_init_session(jobmgr_t jm, const char *session_type, bool sflag)
                (void)jobmgr_assumes(jm, job_setup_machport(bootstrapper));
        } else if (bootstrapper && strncmp(session_type, VPROCMGR_SESSION_SYSTEM, sizeof(VPROCMGR_SESSION_SYSTEM)) == 0) {
 #if TARGET_OS_EMBEDDED
-               bootstrapper->pstype = POSIX_SPAWN_IOS_INTERACTIVE;
+               bootstrapper->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_INTERACTIVE;
 #endif
                bootstrapper->is_bootstrapper = true;
                if (jobmgr_assumes(jm, pid1_magic)) {
@@ -6580,6 +7106,15 @@ jobmgr_delete_anything_with_port(jobmgr_t jm, mach_port_t port)
                return jobmgr_shutdown(jm);
        }
 
+       struct waiting4attach *w4ai = NULL;
+       struct waiting4attach *w4ait = NULL;
+       LIST_FOREACH_SAFE(w4ai, &jm->attaches, le, w4ait) {
+               if (port == w4ai->port) {
+                       waiting4attach_delete(jm, w4ai);
+                       break;
+               }
+       }
+
        return jm;
 }
 
@@ -6811,7 +7346,7 @@ mach_cmd2argv(const char *string)
        argv_ret = malloc((nargs + 1) * sizeof(char *) + strlen(string) + 1);
 
        if (!argv_ret) {
-               (void)osx_assumes_zero(errno);
+               (void)os_assumes_zero(errno);
                return NULL;
        }
 
@@ -6895,9 +7430,16 @@ job_ack_port_destruction(mach_port_t p)
                machservice_resetport(j, ms);
        }
 
+       kern_return_t kr = mach_port_set_attributes(mach_task_self(), ms->port, MACH_PORT_TEMPOWNER, NULL, 0);
+       (void)job_assumes_zero(j, kr);
        machservice_stamp_port(j, ms);
        job_dispatch(j, false);
 
+       if (ms->recv_race_hack) {
+               ms->recv_race_hack = false;
+               machservice_watch(ms->job, ms);
+       }
+
        root_jobmgr = jobmgr_do_garbage_collection(root_jobmgr);
 
        return true;
@@ -7050,7 +7592,7 @@ semaphoreitem_setup(launch_data_t obj, const char *key, void *context)
 }
 
 bool
-externalevent_new(job_t j, struct eventsystem *sys, const char *evname, xpc_object_t event)
+externalevent_new(job_t j, struct eventsystem *sys, const char *evname, xpc_object_t event, uint64_t flags)
 {
        if (j->event_monitor) {
                job_log(j, LOG_ERR, "The event monitor job cannot use LaunchEvents or XPC Events.");
@@ -7071,6 +7613,13 @@ externalevent_new(job_t j, struct eventsystem *sys, const char *evname, xpc_obje
        ee->wanted_state = true;
        sys->curid++;
 
+       if (flags & XPC_EVENT_FLAG_ENTITLEMENTS) {
+               struct ldcred *ldc = runtime_get_caller_creds();
+               if (ldc) {
+                       ee->entitlements = xpc_copy_entitlements_for_pid(ldc->pid);
+               }
+       }
+
        if (sys == _launchd_support_system) {
                ee->internal = true;
        }
@@ -7088,6 +7637,9 @@ void
 externalevent_delete(struct externalevent *ee)
 {
        xpc_release(ee->event);
+       if (ee->entitlements) {
+               xpc_release(ee->entitlements);
+       }
        LIST_REMOVE(ee, job_le);
        LIST_REMOVE(ee, sys_le);
 
@@ -7107,7 +7659,7 @@ externalevent_setup(launch_data_t obj, const char *key, void *context)
        xpc_object_t xobj = ld2xpc(obj);
        if (xobj) {
                job_log(ctx->j, LOG_DEBUG, "Importing stream/event: %s/%s", ctx->sys->name, key);
-               externalevent_new(ctx->j, ctx->sys, key, xobj);
+               externalevent_new(ctx->j, ctx->sys, key, xobj, 0);
                xpc_release(xobj);
        } else {
                job_log(ctx->j, LOG_ERR, "Could not import event for job: %s", key);
@@ -7142,7 +7694,7 @@ eventsystem_new(const char *name)
                (void)strcpy(es->name, name);
                LIST_INSERT_HEAD(&_s_event_systems, es, global_le);
        } else {
-               (void)osx_assumes_zero(errno);
+               (void)os_assumes_zero(errno);
        }
 
        return es;
@@ -7817,7 +8369,7 @@ job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inv
                }
                break;
        case VPROC_GSK_GLOBAL_UMASK:
-               __OSX_COMPILETIME_ASSERT__(sizeof (mode_t) == 2);
+               __OS_COMPILETIME_ASSERT__(sizeof (mode_t) == 2);
                if (inval < 0 || inval > UINT16_MAX) {
                        kr = 1;
                } else {
@@ -7926,16 +8478,14 @@ job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inv
 kern_return_t
 job_mig_post_fork_ping(job_t j, task_t child_task, mach_port_t *asport)
 {
-       struct machservice *ms;
-
        if (!j) {
                return BOOTSTRAP_NO_MEMORY;
        }
 
        job_log(j, LOG_DEBUG, "Post fork ping.");
 
+       struct machservice *ms;
        job_setup_exception_port(j, child_task);
-
        SLIST_FOREACH(ms, &special_ports, special_port_sle) {
                if (j->per_user && (ms->special_port_num != TASK_ACCESS_PORT)) {
                        // The TASK_ACCESS_PORT funny business is to workaround 5325399.
@@ -7964,7 +8514,8 @@ job_mig_post_fork_ping(job_t j, task_t child_task, mach_port_t *asport)
                }
        }
 
-       /* MIG will not zero-initialize this pointer, so we must always do so. See
+       /* MIG will not zero-initialize this pointer, so we must always do so.
+        *
         * <rdar://problem/8562593>.
         */
        *asport = MACH_PORT_NULL;
@@ -7984,6 +8535,67 @@ job_mig_post_fork_ping(job_t j, task_t child_task, mach_port_t *asport)
        return 0;
 }
 
+kern_return_t
+job_mig_get_listener_port_rights(job_t j, mach_port_array_t *sports, mach_msg_type_number_t *sports_cnt)
+{
+       if (!j) {
+               return BOOTSTRAP_NO_MEMORY;
+       }
+
+       size_t cnt = 0;
+       struct machservice *msi = NULL;
+       SLIST_FOREACH(msi, &j->machservices, sle) {
+               if (msi->upfront && job_assumes(j, msi->recv)) {
+                       cnt++;
+               }
+       }
+
+       if (cnt == 0) {
+               return BOOTSTRAP_UNKNOWN_SERVICE;
+       }
+
+       mach_port_array_t sports2 = NULL;
+       mig_allocate((vm_address_t *)&sports2, cnt * sizeof(sports2[0]));
+       if (!sports2) {
+               return BOOTSTRAP_NO_MEMORY;
+       }
+
+       size_t i = 0;
+       SLIST_FOREACH(msi, &j->machservices, sle) {
+               if (msi->upfront && msi->recv) {
+                       sports2[i] = msi->port;
+                       i++;
+               }
+       }
+
+       *sports = sports2;
+       *sports_cnt = cnt;
+
+       return KERN_SUCCESS;
+}
+
+kern_return_t
+job_mig_register_gui_session(job_t j, mach_port_t asport)
+{
+       if (!j->per_user) {
+               return BOOTSTRAP_NOT_PRIVILEGED;
+       }
+
+       jobmgr_t jm = jobmgr_find_xpc_per_user_domain(root_jobmgr, j->mach_uid);
+       if (!jm) {
+               return BOOTSTRAP_UNKNOWN_SERVICE;
+       }
+
+       if (jm->req_gui_asport) {
+               // This job manager persists, so we need to allow the per-user launchd
+               // to update the GUI session as it comes and goes.
+               jobmgr_assumes_zero(jm, launchd_mport_deallocate(jm->req_gui_asport));
+       }
+
+       jm->req_gui_asport = asport;
+       return KERN_SUCCESS;
+}
+
 kern_return_t
 job_mig_reboot2(job_t j, uint64_t flags)
 {
@@ -8291,6 +8903,11 @@ job_mig_check_in2(job_t j, name_t servicename, mach_port_t *serviceportp, uuid_t
                if (job_assumes(j, !j->dedicated_instance)) {
                        *serviceportp = MACH_PORT_NULL;
 
+#if HAVE_SANDBOX
+                       if (unlikely(sandbox_check(ldc->pid, "mach-register", per_pid_service ? SANDBOX_FILTER_LOCAL_NAME : SANDBOX_FILTER_GLOBAL_NAME, servicename) > 0)) {
+                               return BOOTSTRAP_NOT_PRIVILEGED;
+                       }
+#endif
                        if (unlikely((ms = machservice_new(j, servicename, serviceportp, per_pid_service)) == NULL)) {
                                return BOOTSTRAP_NO_MEMORY;
                        }
@@ -8338,17 +8955,24 @@ job_mig_register2(job_t j, name_t servicename, mach_port_t serviceport, uint64_t
 {
        struct machservice *ms;
        struct ldcred *ldc = runtime_get_caller_creds();
+       bool per_pid_service = flags & BOOTSTRAP_PER_PID_SERVICE;
 
        if (!j) {
                return BOOTSTRAP_NO_MEMORY;
        }
 
-       if (!(flags & BOOTSTRAP_PER_PID_SERVICE) && !j->legacy_LS_job) {
+       if (!per_pid_service && !j->legacy_LS_job) {
                job_log(j, LOG_APPLEONLY, "Performance: bootstrap_register() is deprecated. Service: %s", servicename);
        }
 
        job_log(j, LOG_DEBUG, "%sMach service registration attempt: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
 
+#if HAVE_SANDBOX
+       if (unlikely(sandbox_check(ldc->pid, "mach-register", per_pid_service ? SANDBOX_FILTER_LOCAL_NAME : SANDBOX_FILTER_GLOBAL_NAME, servicename) > 0)) {
+               return BOOTSTRAP_NOT_PRIVILEGED;
+       }
+#endif
+
        // 5641783 for the embedded hack
 #if !TARGET_OS_EMBEDDED
        /*
@@ -8599,6 +9223,13 @@ job_mig_info(job_t j, name_array_t *servicenamesp,
                return BOOTSTRAP_NO_MEMORY;
        }
 
+#if TARGET_OS_EMBEDDED
+       struct ldcred *ldc = runtime_get_caller_creds();
+       if (ldc->euid) {
+               return EPERM;
+       }
+#endif // TARGET_OS_EMBEDDED
+
        if (launchd_flat_mach_namespace) {
                if ((j->mgr->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET) || (flags & BOOTSTRAP_FORCE_LOCAL)) {
                        jm = j->mgr;
@@ -8720,20 +9351,21 @@ job_mig_lookup_children(job_t j, mach_port_array_t *child_ports,
        }
 
        mach_port_array_t _child_ports = NULL;
+       name_array_t _child_names = NULL;
+       bootstrap_property_array_t _child_properties = NULL;
+
        mig_allocate((vm_address_t *)&_child_ports, cnt * sizeof(_child_ports[0]));
        if (!job_assumes(j, _child_ports != NULL)) {
                kr = BOOTSTRAP_NO_MEMORY;
                goto out_bad;
        }
 
-       name_array_t _child_names = NULL;
        mig_allocate((vm_address_t *)&_child_names, cnt * sizeof(_child_names[0]));
        if (!job_assumes(j, _child_names != NULL)) {
                kr = BOOTSTRAP_NO_MEMORY;
                goto out_bad;
        }
 
-       bootstrap_property_array_t _child_properties = NULL;
        mig_allocate((vm_address_t *)&_child_properties, cnt * sizeof(_child_properties[0]));
        if (!job_assumes(j, _child_properties != NULL)) {
                kr = BOOTSTRAP_NO_MEMORY;
@@ -8795,7 +9427,7 @@ out_bad:
        }
 
        if (_child_names) {
-               mig_deallocate((vm_address_t)_child_names, cnt * sizeof(_child_ports[0]));
+               mig_deallocate((vm_address_t)_child_names, cnt * sizeof(_child_names[0]));
        }
 
        if (_child_properties) {
@@ -8983,7 +9615,7 @@ job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type, mac
        }
 
        if (launch_data_array_get_count(out_obj_array) != l2l_port_cnt) {
-               osx_assert_zero(l2l_port_cnt);
+               os_assert_zero(l2l_port_cnt);
        }
 
        if (!job_assumes(j, (jmr = jobmgr_new(j->mgr, reqport, rcvright, false, session_type, false, asport)) != NULL)) {
@@ -8991,6 +9623,14 @@ job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type, mac
                goto out;
        }
 
+       if (strcmp(session_type, VPROCMGR_SESSION_AQUA) == 0) {
+               jobmgr_log(jmr, LOG_NOTICE, "Registering new GUI session.");
+               kr = vproc_mig_register_gui_session(inherited_bootstrap_port, asport);
+               if (kr) {
+                       jobmgr_log(jmr, LOG_ERR, "Failed to register GUI session with PID 1: 0x%x/0x%x", inherited_bootstrap_port, kr);
+               }
+       }
+
        jmr->properties |= BOOTSTRAP_PROPERTY_MOVEDSUBSET;
 
        /* This is a hack. We should be doing this in jobmgr_new(), but since we're in the middle of
@@ -9110,7 +9750,9 @@ job_mig_init_session(job_t j, name_t session_type, mach_port_t asport)
                                break;
                        }
                }
-       }
+       } else if (strcmp(session_type, VPROCMGR_SESSION_AQUA) == 0) {
+               (void)job_assumes_zero(j, runtime_remove_mport(j->mgr->jm_port));
+       }
 
        jobmgr_log(j->mgr, LOG_DEBUG, "Initializing as %s", session_type);
        strcpy(j->mgr->name_init, session_type);
@@ -9633,8 +10275,8 @@ xpc_domain_set_environment(job_t j, mach_port_t rp, mach_port_t bsport, mach_por
        }
 
        struct ldcred *ldc = runtime_get_caller_creds();
-       struct proc_bsdshortinfo proc;
-       if (proc_pidinfo(ldc->pid, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) {
+       struct proc_bsdinfowithuniqid proc;
+       if (proc_pidinfo(ldc->pid, PROC_PIDT_BSDINFOWITHUNIQID, 1, &proc, PROC_PIDT_BSDINFOWITHUNIQID_SIZE) == 0) {
                if (errno != ESRCH) {
                        (void)jobmgr_assumes_zero(jm, errno);
                }
@@ -9655,8 +10297,19 @@ xpc_domain_set_environment(job_t j, mach_port_t rp, mach_port_t bsport, mach_por
        jm->req_asport = MACH_PORT_DEAD;
 #endif
 
-       (void)snprintf(jm->name_init, NAME_MAX, "com.apple.xpc.domain.%s[%i]", proc.pbsi_comm, ldc->pid);
-       strlcpy(jm->owner, proc.pbsi_comm, sizeof(jm->owner));
+       struct waiting4attach *w4ai = NULL;
+       struct waiting4attach *w4ait = NULL;
+       LIST_FOREACH_SAFE(w4ai, &_launchd_domain_waiters, le, w4ait) {
+               if (w4ai->dest == ldc->pid) {
+                       jobmgr_log(jm, LOG_DEBUG, "Migrating attach for: %s", w4ai->name);
+                       LIST_REMOVE(w4ai, le);
+                       LIST_INSERT_HEAD(&jm->attaches, w4ai, le);
+                       w4ai->dest = 0;
+               }
+       }
+
+       (void)snprintf(jm->name_init, NAME_MAX, "com.apple.xpc.domain.%s.%d", proc.pbsd.pbi_comm, ldc->pid);
+       strlcpy(jm->owner, proc.pbsd.pbi_comm, sizeof(jm->owner));
        jm->req_bsport = bsport;
        jm->req_excport = excport;
        jm->req_rport = rp;
@@ -9666,6 +10319,7 @@ xpc_domain_set_environment(job_t j, mach_port_t rp, mach_port_t bsport, mach_por
        jm->req_euid = ldc->euid;
        jm->req_egid = ldc->egid;
        jm->req_asid = ldc->asid;
+       jm->req_uniqueid = proc.p_uniqidentifier.p_uniqueid;
 
        return KERN_SUCCESS;
 }
@@ -9738,7 +10392,17 @@ xpc_domain_check_in(job_t j, mach_port_t *bsport, mach_port_t *sbsport,
        *bsport = jm->req_bsport;
        *sbsport = root_jobmgr->jm_port;
        *excport = jm->req_excport;
-       *asport = jm->req_asport;
+       if (j->joins_gui_session) {
+               if (jm->req_gui_asport) {
+                       *asport = jm->req_gui_asport;
+               } else {
+                       job_log(j, LOG_NOTICE, "No GUI session set for UID of user service. This service may not act properly.");
+                       *asport = jm->req_asport;
+               }
+       } else {
+               *asport = jm->req_asport;
+       }
+
        *uid = jm->req_euid;
        *gid = jm->req_egid;
        *asid = jm->req_asid;
@@ -9755,18 +10419,30 @@ xpc_domain_get_service_name(job_t j, event_name_t name)
        if (!j) {
                return BOOTSTRAP_NO_MEMORY;
        }
+
        if (!j->xpc_service) {
                jobmgr_log(j->mgr, LOG_ERR, "Attempt to get service name by non-XPC service: %s", j->label);
                return BOOTSTRAP_NOT_PRIVILEGED;
        }
 
-       struct machservice * ms = SLIST_FIRST(&j->machservices);
-       if (!ms) {
-               jobmgr_log(j->mgr, LOG_ERR, "Attempt to get service name of job with no MachServices: %s", j->label);
+       const char *what2find = j->label;
+       if (j->dedicated_instance) {
+               what2find = j->original->label;
+       }
+
+       struct machservice *msi = NULL;
+       SLIST_FOREACH(msi, &j->machservices, sle) {
+               if (strcmp(msi->name, what2find) == 0) {
+                       break;
+               }
+       }
+
+       if (!msi) {
+               jobmgr_log(j->mgr, LOG_ERR, "Attempt to get service name that does not exist: %s", j->label);
                return BOOTSTRAP_UNKNOWN_SERVICE;
        }
 
-       (void)strlcpy(name, ms->name, sizeof(event_name_t));
+       (void)strlcpy(name, msi->name, sizeof(event_name_t));
        return BOOTSTRAP_SUCCESS;
 }
 
@@ -9883,6 +10559,40 @@ xpc_event_get_event_name(job_t j, xpc_object_t request, xpc_object_t *reply)
        return result;
 }
 
+int
+xpc_event_copy_entitlements(job_t j, xpc_object_t request, xpc_object_t *reply)
+{
+       const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM);
+       if (!stream) {
+               return EXINVAL;
+       }
+
+       uint64_t token = xpc_dictionary_get_uint64(request, XPC_EVENT_ROUTINE_KEY_TOKEN);
+       if (!token) {
+               return EXINVAL;
+       }
+
+       job_log(j, LOG_DEBUG, "Getting entitlements for stream/token: %s/0x%llu", stream, token);
+
+       int result = ESRCH;
+       struct externalevent *event = externalevent_find(stream, token);
+       if (event && j->event_monitor) {
+               xpc_object_t reply2 = xpc_dictionary_create_reply(request);
+               xpc_dictionary_set_value(reply2, XPC_EVENT_ROUTINE_KEY_ENTITLEMENTS, event->entitlements);
+               *reply = reply2;
+
+               job_log(j, LOG_DEBUG, "Found: %s", event->name);
+               result = 0;
+       }
+
+       return result;
+}
+
+// TODO - can be removed with rdar://problem/12666150
+#ifndef XPC_EVENT_FLAG_ALLOW_UNMANAGED
+#define XPC_EVENT_FLAG_ALLOW_UNMANAGED (1 << 1)
+#endif
+       
 int
 xpc_event_set_event(job_t j, xpc_object_t request, xpc_object_t *reply)
 {
@@ -9901,6 +10611,16 @@ xpc_event_set_event(job_t j, xpc_object_t request, xpc_object_t *reply)
                return EXINVAL;
        }
 
+       uint64_t flags = xpc_dictionary_get_uint64(request, XPC_EVENT_ROUTINE_KEY_FLAGS);
+
+       /* Don't allow events to be set for anonymous jobs unless specifically
+        * requested in the flags. Only permit this for internal development.
+        */
+       if (j->anonymous && ((flags & XPC_EVENT_FLAG_ALLOW_UNMANAGED) == 0 || !launchd_apple_internal)) {
+               job_log(j, LOG_ERR, "Unmanaged jobs may not make XPC Events requests.");
+               return EPERM;
+       }
+
        job_log(j, LOG_DEBUG, "%s event for stream/key: %s/%s", event ? "Setting" : "Removing", stream, key);
 
        struct externalevent *eei = NULL;
@@ -9925,7 +10645,7 @@ xpc_event_set_event(job_t j, xpc_object_t request, xpc_object_t *reply)
 
                if (es) {
                        job_log(j, LOG_DEBUG, "Adding event.");
-                       if (externalevent_new(j, es, key, event)) {
+                       if (externalevent_new(j, es, key, event, flags)) {
                                job_log(j, LOG_DEBUG, "Added new event for key: %s", key);
                                result = 0;
                        } else {
@@ -10077,9 +10797,9 @@ xpc_event_provider_check_in(job_t j, xpc_object_t request, xpc_object_t *reply)
                return EPERM;
        }
 
-       /* This indicates that the event monitor is now safe to signal. This state is
-        * independent of whether this operation actually succeeds; we just need it
-        * to ignore SIGUSR1.
+       /* This indicates that the event monitor is now safe to signal. This state
+        * is independent of whether this operation actually succeeds; we just need
+        * it to ignore SIGUSR1.
         */
        j->event_monitor_ready2signal = true;
 
@@ -10188,9 +10908,13 @@ xpc_event_demux(mach_port_t p, xpc_object_t request, xpc_object_t *reply)
        xpc_dictionary_get_audit_token(request, &token);
        runtime_record_caller_creds(&token);
 
-       job_t j = job_mig_intran(p);
-       if (!j || j->anonymous) {
-               op = -1;
+       struct ldcred *ldc = runtime_get_caller_creds();
+       job_t j = managed_job(ldc->pid);
+       if (!j) {
+               j = job_mig_intran(p);
+               if (!j) {
+                       op = -1;
+               }
        }
 
        job_log(j, LOG_DEBUG, "Incoming XPC event request: %llu", op);
@@ -10218,11 +10942,11 @@ xpc_event_demux(mach_port_t p, xpc_object_t request, xpc_object_t *reply)
        case XPC_EVENT_PROVIDER_SET_STATE:
                error = xpc_event_provider_set_state(j, request, reply);
                break;
+       case XPC_EVENT_COPY_ENTITLEMENTS:
+               error = xpc_event_copy_entitlements(j, request, reply);
+               break;
        case -1:
-               if (j) {
-                       job_log(j, LOG_ERR, "Unmanaged jobs may not make XPC Events requests.");
-               }
-               error = EPERM;
+               error = EINVAL;
                break;
        default:
                job_log(j, LOG_ERR, "Bogus opcode.");
@@ -10238,6 +10962,447 @@ xpc_event_demux(mach_port_t p, xpc_object_t request, xpc_object_t *reply)
        return true;
 }
 
+uint64_t
+xpc_get_jetsam_entitlement(const char *key)
+{
+       uint64_t entitlement = 0;
+
+       audit_token_t *token = runtime_get_caller_token();
+       xpc_object_t value = xpc_copy_entitlement_for_token(key, token);
+       if (value) {
+               if (xpc_get_type(value) == XPC_TYPE_UINT64) {
+                       entitlement = xpc_uint64_get_value(value);
+               }
+
+               xpc_release(value);
+       }
+       
+       return entitlement;     
+}
+
+int
+xpc_process_set_jetsam_band(job_t j, xpc_object_t request, xpc_object_t *reply)
+{      
+       if (!j) {
+               return EINVAL;
+       }
+
+       const char *label = xpc_dictionary_get_string(request, XPC_PROCESS_ROUTINE_KEY_LABEL);
+       if (!label) {
+               return EXINVAL;
+       }
+
+       xpc_jetsam_band_t entitled_band = -1;
+       xpc_jetsam_band_t requested_band = (xpc_jetsam_band_t)xpc_dictionary_get_uint64(request, XPC_PROCESS_ROUTINE_KEY_PRIORITY_BAND);
+       if (!requested_band) {
+               return EXINVAL;
+       }
+
+       if (!(requested_band >= XPC_JETSAM_BAND_SUSPENDED && requested_band < XPC_JETSAM_BAND_LAST)) {
+               return EXINVAL;
+       }
+
+       uint64_t rcdata = xpc_dictionary_get_uint64(request, XPC_PROCESS_ROUTINE_KEY_RCDATA);
+       
+       job_t tj = job_find(root_jobmgr, label);
+       if (!tj) {
+               return EXSRCH;
+       }
+
+       boolean_t allow = false;
+       if (j->embedded_god) {
+               allow = true;
+       } else {
+               entitled_band = xpc_get_jetsam_entitlement("com.apple.private.jetsam.modify-priority");
+               if (entitled_band >= requested_band) {
+                       allow = true;
+               }
+       }
+
+       if (!allow) {
+               if (launchd_no_jetsam_perm_check) {
+                       job_log(j, LOG_NOTICE, "Jetsam priority checks disabled; allowing job to set priority: %d", requested_band);
+               } else {                
+                       job_log(j, LOG_ERR, "Job cannot decrease Jetsam priority band (requested/maximum): %d/%d", requested_band, entitled_band);
+                       return EPERM;
+               }
+       }
+
+       job_log(j, LOG_INFO, "Setting Jetsam band: %d.", requested_band);
+       job_update_jetsam_properties(tj, requested_band, rcdata);
+
+       xpc_object_t reply2 = xpc_dictionary_create_reply(request);
+       *reply = reply2;
+
+       return 0;
+}
+
+int
+xpc_process_set_jetsam_memory_limit(job_t j, xpc_object_t request, xpc_object_t *reply)
+{
+       if (!j) {
+               return EINVAL;
+       }
+
+       const char *label = xpc_dictionary_get_string(request, XPC_PROCESS_ROUTINE_KEY_LABEL);
+       if (!label) {
+               return EXINVAL;
+       }
+
+       int32_t entitlement_limit = 0;
+       int32_t requested_limit = (int32_t)xpc_dictionary_get_uint64(request, XPC_PROCESS_ROUTINE_KEY_MEMORY_LIMIT);
+
+       job_t tj = job_find(root_jobmgr, label);
+       if (!tj) {
+               return EXSRCH;
+       }
+
+       boolean_t allow = false;
+       if (j->embedded_god) {
+               allow = true;
+       } else {
+               entitlement_limit = (int32_t)xpc_get_jetsam_entitlement("com.apple.private.jetsam.memory_limit");               
+               if (entitlement_limit >= requested_limit) {
+                       allow = true;
+               }
+       }
+
+       if (!allow) {
+               if (launchd_no_jetsam_perm_check) {
+                       job_log(j, LOG_NOTICE, "Jetsam priority checks disabled; allowing job to set memory limit: %d", requested_limit);
+               } else {                
+                       job_log(j, LOG_ERR, "Job cannot set Jetsam memory limit (requested/maximum): %d/%d", requested_limit, entitlement_limit);
+                       return EPERM;
+               }
+       }
+
+       job_log(j, LOG_INFO, "Setting Jetsam memory limit: %d.", requested_limit);
+       job_update_jetsam_memory_limit(tj, requested_limit);
+
+       xpc_object_t reply2 = xpc_dictionary_create_reply(request);
+       *reply = reply2;
+
+       return 0;
+}
+
+static jobmgr_t
+_xpc_process_find_target_manager(job_t j, xpc_service_type_t type, pid_t pid)
+{
+       jobmgr_t target = NULL;
+       if (type == XPC_SERVICE_TYPE_BUNDLED) {
+               job_log(j, LOG_DEBUG, "Bundled service. Searching for XPC domains for PID: %d", pid);
+
+               jobmgr_t jmi = NULL;
+               SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
+                       if (jmi->req_pid && jmi->req_pid == pid) {
+                               jobmgr_log(jmi, LOG_DEBUG, "Found job manager for PID.");
+                               target = jmi;
+                               break;
+                       }
+               }
+       } else if (type == XPC_SERVICE_TYPE_LAUNCHD || type == XPC_SERVICE_TYPE_APP) {
+               target = j->mgr;
+       }
+
+       return target;
+}
+
+static int
+xpc_process_attach(job_t j, xpc_object_t request, xpc_object_t *reply)
+{
+       if (!j) {
+               return EINVAL;
+       }
+
+       audit_token_t *token = runtime_get_caller_token();
+       xpc_object_t entitlement = xpc_copy_entitlement_for_token(XPC_SERVICE_ENTITLEMENT_ATTACH, token);
+       if (!entitlement) {
+               job_log(j, LOG_ERR, "Job does not have entitlement: %s", XPC_SERVICE_ENTITLEMENT_ATTACH);
+               return EPERM;
+       }
+
+       if (entitlement != XPC_BOOL_TRUE) {
+               char *desc = xpc_copy_description(entitlement);
+               job_log(j, LOG_ERR, "Job has bad value for entitlement: %s:\n%s", XPC_SERVICE_ENTITLEMENT_ATTACH, desc);
+               free(desc);
+
+               xpc_release(entitlement);
+               return EPERM;
+       }
+
+       const char *name = xpc_dictionary_get_string(request, XPC_PROCESS_ROUTINE_KEY_NAME);
+       if (!name) {
+               return EXINVAL;
+       }
+
+       xpc_service_type_t type = xpc_dictionary_get_int64(request, XPC_PROCESS_ROUTINE_KEY_TYPE);
+       if (!type) {
+               return EXINVAL;
+       }
+
+       mach_port_t port = xpc_dictionary_copy_mach_send(request, XPC_PROCESS_ROUTINE_KEY_NEW_INSTANCE_PORT);
+       if (!MACH_PORT_VALID(port)) {
+               return EXINVAL;
+       }
+
+       pid_t pid = xpc_dictionary_get_int64(request, XPC_PROCESS_ROUTINE_KEY_HANDLE);
+
+       job_log(j, LOG_DEBUG, "Attaching to service: %s", name);
+
+       xpc_object_t reply2 = xpc_dictionary_create_reply(request);
+       jobmgr_t target = _xpc_process_find_target_manager(j, type, pid);
+       if (target) {
+               jobmgr_log(target, LOG_DEBUG, "Found target job manager for service: %s", name);
+               (void)jobmgr_assumes(target, waiting4attach_new(target, name, port, 0, type));
+
+               /* HACK: This is awful. For legacy reasons, launchd job labels are all
+                * stored in a global namespace, which is stored in the root job
+                * manager. But XPC domains have a per-domain namespace. So if we're
+                * looking for a legacy launchd job, we have to redirect any attachment
+                * attempts to the root job manager to find existing instances.
+                *
+                * But because we store attachments on a per-job manager basis, we have
+                * to create the new attachment in the actual target job manager, hence
+                * why we change the target only after we've created the attachment.
+                */
+               if (strcmp(target->name, VPROCMGR_SESSION_AQUA) == 0) {
+                       target = root_jobmgr;
+               }
+
+               job_t existing = job_find(target, name);
+               if (existing && existing->p) {
+                       job_log(existing, LOG_DEBUG, "Found existing instance of service.");
+                       xpc_dictionary_set_int64(reply2, XPC_PROCESS_ROUTINE_KEY_PID, existing->p);
+               } else {
+                       xpc_dictionary_set_uint64(reply2, XPC_PROCESS_ROUTINE_KEY_ERROR, ESRCH);
+               }
+       } else if (type == XPC_SERVICE_TYPE_BUNDLED) {
+               (void)job_assumes(j, waiting4attach_new(target, name, port, pid, type));
+               xpc_dictionary_set_uint64(reply2, XPC_PROCESS_ROUTINE_KEY_ERROR, ESRCH);
+       } else {
+               xpc_dictionary_set_uint64(reply2, XPC_PROCESS_ROUTINE_KEY_ERROR, EXSRCH);
+       }
+
+       *reply = reply2;
+       return 0;
+}
+
+static int
+xpc_process_detach(job_t j, xpc_object_t request, xpc_object_t *reply __unused)
+{
+       if (!j) {
+               return EINVAL;
+       }
+
+       const char *name = xpc_dictionary_get_string(request, XPC_PROCESS_ROUTINE_KEY_NAME);
+       if (!name) {
+               return EXINVAL;
+       }
+
+       xpc_service_type_t type = xpc_dictionary_get_int64(request, XPC_PROCESS_ROUTINE_KEY_TYPE);
+       if (!type) {
+               return EXINVAL;
+       }
+
+       job_log(j, LOG_DEBUG, "Deatching from service: %s", name);
+
+       pid_t pid = xpc_dictionary_get_int64(request, XPC_PROCESS_ROUTINE_KEY_PID);
+       jobmgr_t target = _xpc_process_find_target_manager(j, type, pid);
+       if (target) {
+               jobmgr_log(target, LOG_DEBUG, "Found target job manager for service: %s", name);
+
+               struct waiting4attach *w4ai = NULL;
+               struct waiting4attach *w4ait = NULL;
+               LIST_FOREACH_SAFE(w4ai, &target->attaches, le, w4ait) {
+                       if (strcmp(name, w4ai->name) == 0) {
+                               jobmgr_log(target, LOG_DEBUG, "Found attachment. Deleting.");
+                               waiting4attach_delete(target, w4ai);
+                               break;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int
+xpc_process_get_properties(job_t j, xpc_object_t request, xpc_object_t *reply)
+{
+       if (j->anonymous) {
+               /* Total hack. libxpc will send requests to the pipe created out of the
+                * process' bootstrap port, so when job_mig_intran() tries to resolve
+                * the process into a job, it'll wind up creating an anonymous job if
+                * the requestor was an XPC service, whose job manager is an XPC domain.
+                */
+               pid_t pid = j->p;
+               jobmgr_t jmi = NULL;
+               SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
+                       if ((j = jobmgr_find_by_pid(jmi, pid, false))) {
+                               break;
+                       }
+               }
+       }
+
+       if (!j || j->anonymous) {
+               return EXINVAL;
+       }
+
+       struct waiting4attach *w4a = waiting4attach_find(j->mgr, j);
+       if (!w4a) {
+               return EXINVAL;
+       }
+
+       xpc_object_t reply2 = xpc_dictionary_create_reply(request);
+       xpc_dictionary_set_uint64(reply2, XPC_PROCESS_ROUTINE_KEY_TYPE, w4a->type);
+       xpc_dictionary_set_mach_send(reply2, XPC_PROCESS_ROUTINE_KEY_NEW_INSTANCE_PORT, w4a->port);
+       if (j->prog) {
+               xpc_dictionary_set_string(reply2, XPC_PROCESS_ROUTINE_KEY_PATH, j->prog);
+       } else {
+               xpc_dictionary_set_string(reply2, XPC_PROCESS_ROUTINE_KEY_PATH, j->argv[0]);
+       }
+
+       if (j->argv) {
+               xpc_object_t xargv = xpc_array_create(NULL, 0);
+
+               size_t i = 0;
+               for (i = 0; i < j->argc; i++) {
+                       if (j->argv[i]) {
+                               xpc_array_set_string(xargv, XPC_ARRAY_APPEND, j->argv[i]);
+                       }
+               }
+
+               xpc_dictionary_set_value(reply2, XPC_PROCESS_ROUTINE_KEY_ARGV, xargv);
+               xpc_release(xargv);
+       }
+
+       *reply = reply2;
+       return 0;
+}
+
+static int
+xpc_process_service_kill(job_t j, xpc_object_t request, xpc_object_t *reply)
+{
+#if XPC_LPI_VERSION >= 20130426
+       if (!j) {
+               return ESRCH;
+       }
+
+       jobmgr_t jm = _xpc_process_find_target_manager(j, XPC_SERVICE_TYPE_BUNDLED, j->p);
+       if (!jm) {
+               return ENOENT;
+       }
+
+       const char *name = xpc_dictionary_get_string(request, XPC_PROCESS_ROUTINE_KEY_NAME);
+       if (!name) {
+               return EINVAL;
+       }
+
+       int64_t whichsig = xpc_dictionary_get_int64(request, XPC_PROCESS_ROUTINE_KEY_SIGNAL);
+       if (!whichsig) {
+               return EINVAL;
+       }
+
+       job_t j2kill = job_find(jm, name);
+       if (!j2kill) {
+               return ESRCH;
+       }
+
+       if (j2kill->alias) {
+               // Only allow for private instances to be killed.
+               return EPERM;
+       }
+
+       struct proc_bsdshortinfo proc;
+       if (proc_pidinfo(j2kill->p, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) {
+               if (errno != ESRCH) {
+                       (void)jobmgr_assumes_zero(root_jobmgr, errno);
+               }
+
+               return errno;
+       }
+
+       struct ldcred *ldc = runtime_get_caller_creds();
+       if (proc.pbsi_uid != ldc->euid) {
+               // Do not allow non-root to kill RoleAccount services running as a
+               // different user.
+               return EPERM;
+       }
+
+       if (!j2kill->p) {
+               return EALREADY;
+       }
+
+       xpc_object_t reply2 = xpc_dictionary_create_reply(request);
+       if (!reply2) {
+               return EINVAL;
+       }
+
+       int error = 0;
+       int ret = kill(j2kill->p, whichsig);
+       if (ret) {
+               error = errno;
+       }
+
+       xpc_dictionary_set_int64(reply2, XPC_PROCESS_ROUTINE_KEY_ERROR, error);
+       *reply = reply2;
+       return 0;
+#else
+       return ENOTSUP;
+#endif
+}
+
+bool
+xpc_process_demux(mach_port_t p, xpc_object_t request, xpc_object_t *reply)
+{
+       uint64_t op = xpc_dictionary_get_uint64(request, XPC_PROCESS_ROUTINE_KEY_OP);
+       if (!op) {
+               return false;
+       }
+
+       audit_token_t token;
+       xpc_dictionary_get_audit_token(request, &token);
+       runtime_record_caller_creds(&token);
+
+       job_t j = job_mig_intran(p);
+       job_log(j, LOG_DEBUG, "Incoming XPC process request: %llu", op);
+
+       int error = -1;
+       switch (op) {
+       case XPC_PROCESS_JETSAM_SET_BAND:
+               error = xpc_process_set_jetsam_band(j, request, reply);
+               break;
+       case XPC_PROCESS_JETSAM_SET_MEMORY_LIMIT:
+               error = xpc_process_set_jetsam_memory_limit(j, request, reply);
+               break;
+       case XPC_PROCESS_SERVICE_ATTACH:
+               error = xpc_process_attach(j, request, reply);
+               break;
+       case XPC_PROCESS_SERVICE_DETACH:
+               error = xpc_process_detach(j, request, reply);
+               break;
+       case XPC_PROCESS_SERVICE_GET_PROPERTIES:
+               error = xpc_process_get_properties(j, request, reply);
+               break;
+       case XPC_PROCESS_SERVICE_KILL:
+               error = xpc_process_service_kill(j, request, reply);
+               break;
+       default:
+               job_log(j, LOG_ERR, "Bogus process opcode.");
+               error = EDOM;
+       }
+
+       if (error) {
+               xpc_object_t reply2 = xpc_dictionary_create_reply(request);
+               if (reply2) {
+                       xpc_dictionary_set_uint64(reply2, XPC_PROCESS_ROUTINE_KEY_ERROR, error);
+               }
+
+               *reply = reply2;
+       }
+
+       return true;
+}
+
 kern_return_t
 job_mig_kickstart(job_t j, name_t targetlabel, pid_t *out_pid, unsigned int flags)
 {
@@ -10592,8 +11757,8 @@ jobmgr_init(bool sflag)
        SLIST_INIT(&s_curious_jobs);
        LIST_INIT(&s_needing_sessions);
 
-       osx_assert((root_jobmgr = jobmgr_new(NULL, MACH_PORT_NULL, MACH_PORT_NULL, sflag, root_session_type, false, MACH_PORT_NULL)) != NULL);
-       osx_assert((_s_xpc_system_domain = jobmgr_new_xpc_singleton_domain(root_jobmgr, "com.apple.xpc.system")) != NULL);
+       os_assert((root_jobmgr = jobmgr_new(NULL, MACH_PORT_NULL, MACH_PORT_NULL, sflag, root_session_type, false, MACH_PORT_NULL)) != NULL);
+       os_assert((_s_xpc_system_domain = jobmgr_new_xpc_singleton_domain(root_jobmgr, "com.apple.xpc.system")) != NULL);
        _s_xpc_system_domain->req_asid = launchd_audit_session;
        _s_xpc_system_domain->req_asport = launchd_audit_port;
        _s_xpc_system_domain->shortdesc = "system";
@@ -10737,10 +11902,20 @@ jetsam_property_setup(launch_data_t obj, const char *key, job_t j)
        job_log(j, LOG_DEBUG, "Setting Jetsam properties for job...");
        if (strcasecmp(key, LAUNCH_JOBKEY_JETSAMPRIORITY) == 0 && launch_data_get_type(obj) == LAUNCH_DATA_INTEGER) {
                j->jetsam_priority = (typeof(j->jetsam_priority))launch_data_get_integer(obj);
+
+#if XPC_LPI_VERSION >= 20120810
+               if (j->jetsam_priority > XPC_JETSAM_PRIORITY_RESERVED && j->jetsam_priority < XPC_JETSAM_PRIORITY_RESERVED + XPC_JETSAM_BAND_LAST) {
+                       size_t band = j->jetsam_priority - XPC_JETSAM_PRIORITY_RESERVED;
+                       j->jetsam_priority = _launchd_priority_map[band - 1].priority;
+               }
+#endif
                job_log(j, LOG_DEBUG, "Priority: %d", j->jetsam_priority);
        } else if (strcasecmp(key, LAUNCH_JOBKEY_JETSAMMEMORYLIMIT) == 0 && launch_data_get_type(obj) == LAUNCH_DATA_INTEGER) {
                j->jetsam_memlimit = (typeof(j->jetsam_memlimit))launch_data_get_integer(obj);
                job_log(j, LOG_DEBUG, "Memory limit: %d", j->jetsam_memlimit);
+       } else if (strcasecmp(key, LAUNCH_JOBKEY_JETSAMMEMORYLIMITBACKGROUND) == 0) {
+               j->jetsam_memory_limit_background = true;
+               job_log(j, LOG_DEBUG, "Memory limit is for background state only");
        } else if (strcasecmp(key, LAUNCH_KEY_JETSAMFRONTMOST) == 0) {
                /* Ignore. We only recognize this key so we don't complain when we get SpringBoard's request. 
                 * You can't set this in a plist.
@@ -10760,74 +11935,39 @@ jetsam_property_setup(launch_data_t obj, const char *key, job_t j)
        }
 }
 
-#if TARGET_OS_EMBEDDED
-int
-launchd_set_jetsam_priorities(launch_data_t priorities)
+void
+job_update_jetsam_properties(job_t j, xpc_jetsam_band_t band, uint64_t user_data)
 {
-       kern_return_t result =  0;
-
-       if (launch_data_get_type(priorities) != LAUNCH_DATA_ARRAY) {
-               return EINVAL;
-       }
-       if (!launchd_embedded_handofgod) {
-               return EPERM;
-       }
-
-       size_t npris = launch_data_array_get_count(priorities);
-
-       job_t ji = NULL;
-       size_t i = 0;
-       for (i = 0; i < npris; i++) {
-               launch_data_t ldi = launch_data_array_get_index(priorities, i);
-               if (launch_data_get_type(ldi) != LAUNCH_DATA_DICTIONARY) {
-                       continue;
-               }
-
-               launch_data_t ldlabel = NULL;
-               if (!(ldlabel = launch_data_dict_lookup(ldi, LAUNCH_KEY_JETSAMLABEL))) {
-                       continue;
-               }
-               const char *label = launch_data_get_string(ldlabel);
-
-               ji = job_find(root_jobmgr, label);
-               if (!ji) {
-                       continue;
-               }
-
-               launch_data_dict_iterate(ldi, (void (*)(launch_data_t, const char *, void *))jetsam_property_setup, ji);
-
-               launch_data_t frontmost = NULL;
-               if ((frontmost = launch_data_dict_lookup(ldi, LAUNCH_KEY_JETSAMFRONTMOST)) && launch_data_get_type(frontmost) == LAUNCH_DATA_BOOL) {
-                       ji->jetsam_frontmost = launch_data_get_bool(frontmost);
-               }
-
-               launch_data_t active = NULL;
-               if ((active = launch_data_dict_lookup(ldi, LAUNCH_KEY_JETSAMACTIVE)) && launch_data_get_type(active) == LAUNCH_DATA_BOOL) {
-                       ji->jetsam_active = launch_data_get_bool(active);
-               }
+#if TARGET_OS_EMBEDDED
+       j->jetsam_priority = _launchd_priority_map[band - 1].priority;
+       j->jetsam_properties = true;
 
-               launchd_update_jetsam_list(ji);
+       memorystatus_priority_properties_t mjp;
+       mjp.priority = j->jetsam_priority;
+       mjp.user_data = user_data;
 
-               result = result != 0 ? errno : 0;       
+       size_t size = sizeof(mjp);
+       int r = memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES, j->p, 0, &mjp, size);
+       if (r == -1 && errno != ESRCH) {
+               (void)job_assumes_zero(j, errno);
        }
-
-       return result;
+#else
+#pragma unused(j, band, user_data)
+#endif
 }
 
-int
-launchd_update_jetsam_list(job_t j)
+void
+job_update_jetsam_memory_limit(job_t j, int32_t limit)
 {
-       memorystatus_priority_entry_t mpe;
-       kern_return_t result;
-
-       mpe.pid = j->p;
-       mpe.priority = j->jetsam_priority;
-       mpe.flags = 0;
-       mpe.flags |= j->jetsam_frontmost ? kMemorystatusFlagsFrontmost : 0;
-       mpe.flags |= j->jetsam_active ? kMemorystatusFlagsActive : 0;
+#if TARGET_OS_EMBEDDED
+       j->jetsam_memlimit = limit;
+       j->jetsam_properties = true;
 
-       // ToDo - cache MIB if we keep this interface
-       (void)posix_assumes_zero(result = sysctlbyname("kern.memorystatus_jetsam_change", NULL, NULL, &mpe, sizeof(memorystatus_priority_entry_t)));
-       return result;
-}
+       int r = memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_HIGH_WATER_MARK, j->p, limit, NULL, 0);
+       if (r == -1 && errno != ESRCH) {
+               (void)job_assumes_zero(j, errno);
+       }
+#else
+#pragma unused(j, limit)
 #endif
+}
index 0bcda0383ca376f1c263b8679a9babb776db00f0..b8267c5ae8407857ab37cfb03476fd876039149a 100644 (file)
@@ -24,6 +24,7 @@
 #include "runtime.h"
 #include "bootstrap.h"
 #include "launch.h"
+#include <xpc/private.h>
 
 typedef struct job_s *job_t;
 typedef struct jobmgr_s *jobmgr_t;
@@ -60,11 +61,7 @@ void job_ack_no_senders(job_t j);
 void job_log(job_t j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
 void job_set_pid_crashed(pid_t p);
 
-#if TARGET_OS_EMBEDDED
-int launchd_set_jetsam_priorities(launch_data_t priorities);
-int launchd_update_jetsam_list(job_t j);
-#endif
-
 bool xpc_event_demux(mach_port_t p, xpc_object_t request, xpc_object_t *reply);
+bool xpc_process_demux(mach_port_t p, xpc_object_t request, xpc_object_t *reply);
 
 #endif /* __LAUNCHD_CORE_LOGIC__ */
index d6a1d0806c4c820d512522286d6287aa4bf5a60d..7d902bdf71cda70c4f89b6e8f7cc5aea39eb306f 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -45,7 +45,7 @@
 #include <stdbool.h>
 #include <paths.h>
 #include <string.h>
-#include <assumes.h>
+#include <os/assumes.h>
 
 #include "launch.h"
 #include "launch_priv.h"
@@ -207,6 +207,21 @@ ipc_listen_callback(void *obj __attribute__((unused)), struct kevent *kev)
                return;
        }
 
+       if (geteuid() == 0) {
+               uid_t euid, guid;
+               if (getpeereid(cfd, &euid, &guid) == -1) {
+                       launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[%d] failed to getpeereid on incoming caller (%d)", getpid(), errno);
+                       (void)runtime_close(cfd);
+                       return;
+               }
+
+               if (euid != geteuid()) {
+                       launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[%d] failed to euid check on incoming caller (%d != %d)", getpid(), euid, geteuid());
+                       (void)runtime_close(cfd);
+                       return;
+               }
+       }
+
        ipc_open(cfd, NULL);
 }
 
@@ -429,12 +444,6 @@ ipc_readmsg2(launch_data_t data, const char *cmd, void *context)
                                        resp = job_export(j);
                                        ipc_revoke_fds(resp);
                                }
-                       } else if (!strcmp(cmd, LAUNCH_KEY_SETPRIORITYLIST)) {
-#if TARGET_OS_EMBEDDED
-                               resp = launch_data_new_errno(launchd_set_jetsam_priorities(data));
-#else
-                               resp = launch_data_new_errno(ENOTSUP);
-#endif
                        }
                }
 #if TARGET_OS_EMBEDDED
index cf40b952e3acbc57917cdaf4742512c645f15098..81b536e9d059ae53ae1837c13f53b03fa8f1a356 100644 (file)
@@ -283,3 +283,15 @@ out                                reply           : pointer_t, dealloc;
 out                            reply_fds       : mach_port_move_send_array_t, dealloc;
                                asport          : mach_port_t
 );
+
+routine
+get_listener_port_rights(
+                               j                       : job_t;
+out                            sports          : mach_port_make_send_array_t, dealloc
+);
+
+routine
+register_gui_session(
+                               j                       : job_t;
+                               asport          : mach_port_t
+);
index f6ba31a30f6b12e31126f9d9755283126841e3e7..77390a0612e2a154e16b9108536b92198a509a7f 100644 (file)
  * Copyright, 1990.  All rights reserved.
  */
 
-/* This really should be a part of the standard types... */
+/* These really should be a part of the standard types... */
 type mach_port_move_send_array_t = array[] of mach_port_move_send_t
        ctype: mach_port_array_t;
+type mach_port_make_send_array_t = array[] of mach_port_make_send_t
+       ctype: mach_port_array_t;
 
 type pid_t = integer_t;
 type pid_array_t = ^array [] of pid_t;
index e9fd0e6a32263d7189c084bcbb44e83cf560d79b..0f8151dbdef120c62d937967d8e5270730a8ca63 100644 (file)
@@ -64,7 +64,7 @@
 #include <sched.h>
 #include <pthread.h>
 #include <util.h>
-#include <assumes.h>
+#include <os/assumes.h>
 
 #if HAVE_LIBAUDITD
 #include <bsm/auditd_lib.h>
@@ -221,8 +221,8 @@ main(int argc, char *const *argv)
                 * <rdar://problem/5039559&6153301>
                 */
                pthread_t t = NULL;
-               (void)osx_assumes_zero(pthread_create(&t, NULL, update_thread, NULL));
-               (void)osx_assumes_zero(pthread_detach(t));
+               (void)os_assumes_zero(pthread_create(&t, NULL, update_thread, NULL));
+               (void)os_assumes_zero(pthread_detach(t));
 
                /* PID 1 doesn't have a flat namespace. */
                launchd_flat_mach_namespace = false;
@@ -409,13 +409,15 @@ fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused)))
 {
        const char *doom_why = "at instruction";
        char msg[128];
+#if 0
        char *sample_args[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE, NULL };
        pid_t sample_p;
        int wstatus;
+#endif
 
        crash_addr = si->si_addr;
        crash_pid = si->si_pid;
-
+#if 0
        setenv("XPC_SERVICES_UNAVAILABLE", "1", 0);
        unlink(PID1_CRASH_LOGFILE);
 
@@ -430,7 +432,7 @@ fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused)))
        case -1:
                break;
        }
-
+#endif
        switch (sig) {
        default:
        case 0:
@@ -440,6 +442,7 @@ fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused)))
                doom_why = "trying to read/write";
        case SIGILL:
        case SIGFPE:
+       case SIGTRAP:
                snprintf(msg, sizeof(msg), "%s: %p (%s sent by PID %u)", doom_why, crash_addr, strsignal(sig), crash_pid);
                sync();
                do_pid1_crash_diagnosis_mode(msg);
@@ -534,11 +537,11 @@ launchd_shutdown(void)
        char *term_who = pid1_magic ? "System shutdown" : "Per-user launchd termination for ";
        launchd_syslog(LOG_INFO, "%s%s began", term_who, pid1_magic ? "" : launchd_username);
 
-       osx_assert(jobmgr_shutdown(root_jobmgr) != NULL);
+       os_assert(jobmgr_shutdown(root_jobmgr) != NULL);
 
 #if HAVE_LIBAUDITD
        if (pid1_magic) {
-               (void)osx_assumes_zero(audit_quick_stop());
+               (void)os_assumes_zero(audit_quick_stop());
        }
 #endif
 }
@@ -625,7 +628,7 @@ monitor_networking_state(void)
        network_up = get_network_state();
 
        if (pfs == -1) {
-               (void)osx_assumes_zero(errno);
+               (void)os_assumes_zero(errno);
                return;
        }
 
index d74e41883179c4418f4bbae8fc89aab1ebbaf9b2..1bd8b4906b1114ba9ef2a643beec6c7cf71494f0 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1,5 +1,5 @@
 #include <dispatch/dispatch.h>
-#include <assumes.h>
+#include <os/assumes.h>
 #include "job_reply.h"
 
 #include "launchd.h"
@@ -13,6 +13,8 @@
 #define LAUNCHD_SHUTDOWN_LOG "launchd-shutdown.%s.log"
 #define LAUNCHD_LOWLEVEL_LOG "launchd-lowlevel.%s.log"
 
+os_redirect_assumes(_launchd_os_redirect);
+
 char *launchd_username = "unknown";
 char *launchd_label = "com.apple.launchd.unknown";
 mach_port_t launchd_drain_reply_port;
@@ -116,7 +118,7 @@ _logmsg_remove(struct logmsg_s *lm)
 }
 
 bool
-_launchd_osx_redirect(const char *message)
+_launchd_os_redirect(const char *message)
 {
        launchd_syslog(LOG_ERR, "%s", message);
        return true;
@@ -292,9 +294,9 @@ _launchd_log_uncork_pending_drain(void)
 
        if (unlikely(errno = job_mig_log_drain_reply(tmp_port, 0, outval, outvalCnt))) {
                if (errno != MACH_SEND_INVALID_DEST) {
-                       (void)osx_assumes_zero(errno);
+                       (void)os_assumes_zero(errno);
                }
-               (void)osx_assumes_zero(launchd_mport_deallocate(tmp_port));
+               (void)os_assumes_zero(launchd_mport_deallocate(tmp_port));
        }
 
        mig_deallocate(outval, outvalCnt);
@@ -374,11 +376,11 @@ launchd_log_forward(uid_t forward_uid, gid_t forward_gid, vm_offset_t inval, mac
 kern_return_t
 launchd_log_drain(mach_port_t srp, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt)
 {
-       (void)osx_assumes_zero(launchd_drain_reply_port);
+       (void)os_assumes_zero(launchd_drain_reply_port);
 
        if ((_launchd_logq_cnt == 0) || launchd_shutting_down) {
                launchd_drain_reply_port = srp;
-               (void)osx_assumes_zero(launchd_mport_notify_req(launchd_drain_reply_port, MACH_NOTIFY_DEAD_NAME));
+               (void)os_assumes_zero(launchd_mport_notify_req(launchd_drain_reply_port, MACH_NOTIFY_DEAD_NAME));
 
                return MIG_NO_REPLY;
        }
index 4afc53dc4b318fb4a4cfccef5596eb28b6ccdafe..517f1811346ccc672cef6629066b5174633613fd 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -43,7 +43,7 @@ struct launchd_syslog_attr {
 __attribute__((visibility("default")))
 __attribute__((used))
 extern bool
-_launchd_osx_redirect(const char *message);
+_launchd_os_redirect(const char *message);
 
 int
 runtime_setlogmask(int maskpri);
index b65b7d109db5df343fe1a566b4bc2c2d53f4b993..026f0eb795dce234c9f08554a2bfd4224fb34707 100644 (file)
@@ -38,6 +38,8 @@
 #include <sys/sysctl.h>
 #include <sys/time.h>
 #include <sys/proc.h>
+#include <sys/proc_info.h>
+#include <libproc.h>
 #include <sys/event.h>
 #include <sys/queue.h>
 #include <sys/socket.h>
@@ -58,7 +60,7 @@
 #include <syslog.h>
 #include <signal.h>
 #include <dlfcn.h>
-#include <assumes.h>
+#include <os/assumes.h>
 
 #include "internalServer.h"
 #include "internal.h"
@@ -101,6 +103,7 @@ static size_t mig_cb_table_sz;
 static timeout_callback runtime_idle_callback;
 static mach_msg_timeout_t runtime_idle_timeout;
 static struct ldcred ldc;
+static audit_token_t ldc_token;
 static size_t runtime_standby_cnt;
 
 static void do_file_init(void) __attribute__((constructor));
@@ -128,8 +131,12 @@ bool launchd_log_shutdown = false;
 bool launchd_log_perf = false;
 bool launchd_log_debug = false;
 bool launchd_trap_sigkill_bugs = false;
+bool launchd_no_jetsam_perm_check = false;
 bool launchd_osinstaller = false;
 bool launchd_allow_global_dyld_envvars = false;
+#if TARGET_OS_EMBEDDED
+bool launchd_appletv = false;
+#endif
 pid_t launchd_wsp = 0;
 size_t runtime_busy_cnt;
 
@@ -179,21 +186,21 @@ launchd_runtime_init(void)
 
        (void)posix_assert_zero((mainkq = kqueue()));
 
-       osx_assert_zero(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &demand_port_set));
-       osx_assert_zero(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &ipc_port_set));
+       os_assert_zero(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &demand_port_set));
+       os_assert_zero(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &ipc_port_set));
        posix_assert_zero(kevent_mod(demand_port_set, EVFILT_MACHPORT, EV_ADD, 0, 0, &kqmportset_callback));
 
-       osx_assert_zero(launchd_mport_create_recv(&launchd_internal_port));
-       osx_assert_zero(launchd_mport_make_send(launchd_internal_port));
+       os_assert_zero(launchd_mport_create_recv(&launchd_internal_port));
+       os_assert_zero(launchd_mport_make_send(launchd_internal_port));
 
        max_msg_size = sizeof(union vproc_mig_max_sz);
        if (sizeof(union xpc_domain_max_sz) > max_msg_size) {
                max_msg_size = sizeof(union xpc_domain_max_sz);
        }
 
-       osx_assert_zero(runtime_add_mport(launchd_internal_port, launchd_internal_demux));
-       osx_assert_zero(pthread_create(&kqueue_demand_thread, NULL, kqueue_demand_loop, NULL));
-       osx_assert_zero(pthread_detach(kqueue_demand_thread));
+       os_assert_zero(runtime_add_mport(launchd_internal_port, launchd_internal_demux));
+       os_assert_zero(pthread_create(&kqueue_demand_thread, NULL, kqueue_demand_loop, NULL));
+       os_assert_zero(pthread_detach(kqueue_demand_thread));
 
        (void)posix_assumes_zero(sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &p, sizeof(p)));
 }
@@ -203,7 +210,7 @@ launchd_runtime_init2(void)
 {
        size_t i;
 
-       __OSX_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1);
+       __OS_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1);
        for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) {
                sigaddset(&sigign_set, sigigns[i]);
                (void)posix_assumes_zero(signal(sigigns[i], SIG_IGN));
@@ -477,7 +484,7 @@ mportset_callback(void)
        struct kevent kev;
        unsigned int i;
 
-       if (osx_assumes_zero(mach_port_get_set_status(mach_task_self(), demand_port_set, &members, &membersCnt)) != 0) {
+       if (os_assumes_zero(mach_port_get_set_status(mach_task_self(), demand_port_set, &members, &membersCnt)) != 0) {
                return;
        }
 
@@ -504,7 +511,7 @@ mportset_callback(void)
                }
        }
 
-       (void)osx_assumes_zero(vm_deallocate(mach_task_self(), (vm_address_t)members, (vm_size_t) membersCnt * sizeof(mach_port_name_t)));
+       (void)os_assumes_zero(vm_deallocate(mach_task_self(), (vm_address_t)members, (vm_size_t) membersCnt * sizeof(mach_port_name_t)));
 }
 
 void *
@@ -525,9 +532,9 @@ kqueue_demand_loop(void *arg __attribute__((unused)))
                FD_SET(mainkq, &rfds);
                int r = select(mainkq + 1, &rfds, NULL, NULL, NULL);
                if (r == 1) {
-                       (void)osx_assumes_zero(handle_kqueue(launchd_internal_port, mainkq));
+                       (void)os_assumes_zero(handle_kqueue(launchd_internal_port, mainkq));
                } else if (posix_assumes_zero(r) != -1) {
-                       (void)osx_assumes_zero(r);
+                       (void)os_assumes_zero(r);
                }
        }
 
@@ -574,7 +581,7 @@ x_handle_kqueue(mach_port_t junk __attribute__((unused)), integer_t fd)
                        }
                }
        } else {
-               (void)osx_assumes_zero(errno);
+               (void)os_assumes_zero(errno);
        }
 
        bulk_kev = NULL;
@@ -620,7 +627,7 @@ launchd_mport_notify_req(mach_port_t name, mach_msg_id_t which)
        errno = mach_port_request_notification(mach_task_self(), name, which, msgc, where, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
 
        if (likely(errno == 0) && previous != MACH_PORT_NULL) {
-               (void)osx_assumes_zero(launchd_mport_deallocate(previous));
+               (void)os_assumes_zero(launchd_mport_deallocate(previous));
        }
 
        return errno;
@@ -636,11 +643,11 @@ runtime_fork(mach_port_t bsport)
 
        sigemptyset(&emptyset);
 
-       (void)osx_assumes_zero(launchd_mport_make_send(bsport));
-       (void)osx_assumes_zero(launchd_set_bport(bsport));
-       (void)osx_assumes_zero(launchd_mport_deallocate(bsport));
+       (void)os_assumes_zero(launchd_mport_make_send(bsport));
+       (void)os_assumes_zero(launchd_set_bport(bsport));
+       (void)os_assumes_zero(launchd_mport_deallocate(bsport));
 
-       __OSX_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1);
+       __OS_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1);
        (void)posix_assumes_zero(sigprocmask(SIG_BLOCK, &sigign_set, &oset));
        for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) {
                (void)posix_assumes_zero(signal(sigigns[i], SIG_DFL));
@@ -654,7 +661,7 @@ runtime_fork(mach_port_t bsport)
                        (void)posix_assumes_zero(signal(sigigns[i], SIG_IGN));
                }
                (void)posix_assumes_zero(sigprocmask(SIG_SETMASK, &oset, NULL));
-               (void)osx_assumes_zero(launchd_set_bport(MACH_PORT_NULL));
+               (void)os_assumes_zero(launchd_set_bport(MACH_PORT_NULL));
        } else {
                pid_t p = -getpid();
                (void)posix_assumes_zero(sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &p, sizeof(p)));
@@ -817,7 +824,7 @@ kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t
                        return -1;
                }
        } else {
-               (void)osx_assert_zero(kev.flags);
+               (void)os_assert_zero(kev.flags);
        }
 
        return r;
@@ -840,7 +847,7 @@ do_mach_notify_port_destroyed(mach_port_t notify __attribute__((unused)), mach_p
 {
        /* This message is sent to us when a receive right is returned to us. */
        if (!job_ack_port_destruction(rights)) {
-               (void)osx_assumes_zero(launchd_mport_close_recv(rights));
+               (void)os_assumes_zero(launchd_mport_close_recv(rights));
        }
 
        return KERN_SUCCESS;
@@ -893,7 +900,7 @@ do_mach_notify_dead_name(mach_port_t notify __attribute__((unused)), mach_port_n
         * receiver somewhere else on the system.
         */
        if (name == launchd_drain_reply_port) {
-               (void)osx_assumes_zero(launchd_mport_deallocate(name));
+               (void)os_assumes_zero(launchd_mport_deallocate(name));
                launchd_drain_reply_port = MACH_PORT_NULL;
        }
 
@@ -904,7 +911,7 @@ do_mach_notify_dead_name(mach_port_t notify __attribute__((unused)), mach_port_n
        /* A dead-name notification about a port appears to increment the rights on
         * said port. Let's deallocate it so that we don't leak dead-name ports.
         */
-       (void)osx_assumes_zero(launchd_mport_deallocate(name));
+       (void)os_assumes_zero(launchd_mport_deallocate(name));
 
        return KERN_SUCCESS;
 }
@@ -928,7 +935,7 @@ launchd_exc_runtime_once(mach_port_t port, mach_msg_size_t rcv_msg_size, mach_ms
                        launchd_syslog(LOG_INFO, "Message is larger than %u bytes.", rcv_msg_size);
                        break;
                default:
-                       (void)osx_assumes_zero(mr);
+                       (void)os_assumes_zero(mr);
                }
 
                if (mr == MACH_MSG_SUCCESS) {
@@ -940,7 +947,7 @@ launchd_exc_runtime_once(mach_port_t port, mach_msg_size_t rcv_msg_size, mach_ms
                        mach_msg_return_t smr = ~MACH_MSG_SUCCESS;
                        mach_msg_option_t send_options = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
 
-                       (void)osx_assumes(bufReply->Head.msgh_size <= send_msg_size);
+                       (void)os_assumes(bufReply->Head.msgh_size <= send_msg_size);
                        smr = mach_msg(&bufReply->Head, send_options, bufReply->Head.msgh_size, 0, MACH_PORT_NULL, to + 100, MACH_PORT_NULL);
                        switch (smr) {
                        case MACH_SEND_TIMED_OUT:
@@ -964,6 +971,7 @@ launchd_exc_runtime_once(mach_port_t port, mach_msg_size_t rcv_msg_size, mach_ms
 void
 runtime_record_caller_creds(audit_token_t *token)
 {
+       (void)memcpy(&ldc_token, token, sizeof(*token));
        audit_token_to_au32(*token, NULL, &ldc.euid,&ldc.egid, &ldc.uid, &ldc.gid,
                &ldc.pid, &ldc.asid, NULL);
 }
@@ -974,6 +982,12 @@ runtime_get_caller_creds(void)
        return &ldc;
 }
 
+audit_token_t *
+runtime_get_caller_token(void)
+{
+       return &ldc_token;
+}
+
 static boolean_t
 launchd_mig_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
 {
@@ -1014,11 +1028,18 @@ launchd_runtime2(mach_msg_size_t msg_size)
                xpc_object_t request = NULL;
                int result = xpc_pipe_try_receive(ipc_port_set, &request, &recvp, launchd_mig_demux, msg_size, 0);
                if (result == 0 && request) {
+                       boolean_t handled = false;
                        time_of_mach_msg_return = runtime_get_opaque_time();
                        launchd_syslog(LOG_DEBUG, "XPC request.");
 
                        xpc_object_t reply = NULL;
-                       if (!xpc_event_demux(recvp, request, &reply)) {
+                       if (xpc_event_demux(recvp, request, &reply)) {
+                               handled = true;
+                       } else if (xpc_process_demux(recvp, request, &reply)) {
+                               handled = true;
+                       }
+
+                       if (!handled) {
                                launchd_syslog(LOG_DEBUG, "XPC routine could not be handled.");
                                xpc_release(request);
                                continue;
@@ -1171,13 +1192,13 @@ catch_mach_exception_raise(mach_port_t exception_port __attribute__((unused)), m
 {
        pid_t p4t = -1;
 
-       (void)osx_assumes_zero(pid_for_task(task, &p4t));
+       (void)os_assumes_zero(pid_for_task(task, &p4t));
 
        launchd_syslog(LOG_NOTICE, "%s(): PID: %u thread: 0x%x type: 0x%x code: %p codeCnt: 0x%x",
                        __func__, p4t, thread, exception, code, codeCnt);
 
-       (void)osx_assumes_zero(launchd_mport_deallocate(thread));
-       (void)osx_assumes_zero(launchd_mport_deallocate(task));
+       (void)os_assumes_zero(launchd_mport_deallocate(thread));
+       (void)os_assumes_zero(launchd_mport_deallocate(task));
 
        return KERN_SUCCESS;
 }
@@ -1205,7 +1226,7 @@ catch_mach_exception_raise_state_identity(mach_port_t exception_port __attribute
 {
        pid_t p4t = -1;
 
-       (void)osx_assumes_zero(pid_for_task(task, &p4t));
+       (void)os_assumes_zero(pid_for_task(task, &p4t));
 
        launchd_syslog(LOG_NOTICE, "%s(): PID: %u thread: 0x%x type: 0x%x code: %p codeCnt: 0x%x flavor: %p old_state: %p old_stateCnt: 0x%x new_state: %p new_stateCnt: %p",
                        __func__, p4t, thread, exception, code, codeCnt, flavor, old_state, old_stateCnt, new_state, new_stateCnt);
@@ -1213,12 +1234,31 @@ catch_mach_exception_raise_state_identity(mach_port_t exception_port __attribute
        memcpy(new_state, old_state, old_stateCnt * sizeof(old_state[0]));
        *new_stateCnt = old_stateCnt;
 
-       (void)osx_assumes_zero(launchd_mport_deallocate(thread));
-       (void)osx_assumes_zero(launchd_mport_deallocate(task));
+       (void)os_assumes_zero(launchd_mport_deallocate(thread));
+       (void)os_assumes_zero(launchd_mport_deallocate(task));
 
        return KERN_SUCCESS;
 }
 
+// FIXME: should this be thread safe? With dispatch_once?
+uint64_t
+runtime_get_uniqueid(void)
+{
+       static bool once;
+       static uint64_t uniqueid;
+       if (unlikely(!once)) {
+               once = true;
+
+               struct proc_uniqidentifierinfo info;
+               int size;
+               size = proc_pidinfo(getpid(), PROC_PIDUNIQIDENTIFIERINFO, 0, &info, sizeof(info));
+               if (size == PROC_PIDUNIQIDENTIFIERINFO_SIZE) {
+                       uniqueid = info.p_uniqueid;
+               }
+       }
+       return uniqueid;
+}
+
 void
 launchd_log_vm_stats(void)
 {
@@ -1230,12 +1270,12 @@ launchd_log_vm_stats(void)
 
        statsp = did_first_pass ? &stats : &orig_stats;
 
-       if (osx_assumes_zero(host_statistics(mhs, HOST_VM_INFO, (host_info_t)statsp, &count)) != KERN_SUCCESS) {
+       if (os_assumes_zero(host_statistics(mhs, HOST_VM_INFO, (host_info_t)statsp, &count)) != KERN_SUCCESS) {
                return;
        }
 
        if (count != HOST_VM_INFO_COUNT) {
-               (void)osx_assumes_zero(count);
+               (void)os_assumes_zero(count);
        }
 
        if (did_first_pass) {
@@ -1337,7 +1377,7 @@ do_file_init(void)
 {
        struct stat sb;
 
-       osx_assert_zero(mach_timebase_info(&tbi));
+       os_assert_zero(mach_timebase_info(&tbi));
        tbi_float_val = tbi.numer;
        tbi_float_val /= tbi.denom;
        tbi_safe_math_max = UINT64_MAX / tbi.numer;
@@ -1376,19 +1416,38 @@ do_file_init(void)
                launchd_allow_global_dyld_envvars = true;
        }
 
-       char bootargs[1024];
-       size_t len = sizeof(bootargs) - 1;
-       int r = pid1_magic ? sysctlbyname("kern.bootargs", bootargs, &len, NULL, 0) : -1;
+       char buff[1024];
+       size_t len = sizeof(buff) - 1;
+       int r = pid1_magic ? sysctlbyname("kern.bootargs", buff, &len, NULL, 0) : -1;
        if (r == 0) {
-               if (strnstr(bootargs, "-v", len)) {
+               if (strnstr(buff, "-v", len)) {
                        launchd_verbose_boot = true;
                }
-               if (strnstr(bootargs, "launchd_trap_sigkill_bugs", len)) {
+               if (strnstr(buff, "launchd_trap_sigkill_bugs", len)) {
                        launchd_trap_sigkill_bugs = true;
                }
+               if (strnstr(buff, "launchd_no_jetsam_perm_check", len)) {
+                       launchd_no_jetsam_perm_check = true;
+               }
+       }
+
+       len = sizeof(buff) - 1;
+#if TARGET_OS_EMBEDDED
+       r = sysctlbyname("hw.machine", buff, &len, NULL, 0);
+       if (r == 0) {
+               if (strnstr(buff, "AppleTV", len)) {
+                       launchd_appletv = true;
+               }
        }
+#endif
 
+#if !TARGET_OS_EMBEDDED
        if (pid1_magic && launchd_verbose_boot && config_check(".launchd_shutdown_debugging", sb)) {
                launchd_shutdown_debugging = true;
        }
+#else
+       if (pid1_magic && config_check(".launchd_shutdown_debugging", sb)) {
+               launchd_shutdown_debugging = true;
+       }
+#endif
 }
index 2b8e9857aabbec565e7a0f133d51ef031566f5a7..a13deff060c351053c498feb2f05063c9c5755ef 100644 (file)
@@ -60,8 +60,12 @@ extern bool launchd_log_shutdown;
 extern bool launchd_log_debug;
 extern bool launchd_log_perf;
 extern bool launchd_trap_sigkill_bugs;
+extern bool launchd_no_jetsam_perm_check;
 extern bool launchd_osinstaller;
 extern bool launchd_allow_global_dyld_envvars;
+#if TARGET_OS_EMBEDDED
+extern bool launchd_appletv;
+#endif
 
 extern bool launchd_runtime_busy_time;
 extern mach_port_t inherited_bootstrap_port;
@@ -95,6 +99,7 @@ kern_return_t runtime_add_mport(mach_port_t name, mig_callback demux);
 kern_return_t runtime_remove_mport(mach_port_t name);
 void runtime_record_caller_creds(audit_token_t *token);
 struct ldcred *runtime_get_caller_creds(void);
+audit_token_t *runtime_get_caller_token(void);
 
 const char *signal_to_C_name(unsigned int sig);
 const char *reboot_flags_to_C_names(unsigned int flags);
@@ -116,7 +121,6 @@ uint64_t runtime_get_nanoseconds_since(uint64_t o) __attribute__((pure, warn_unu
 kern_return_t launchd_set_bport(mach_port_t name);
 kern_return_t launchd_get_bport(mach_port_t *name);
 kern_return_t launchd_mport_notify_req(mach_port_t name, mach_msg_id_t which);
-kern_return_t launchd_mport_notify_cancel(mach_port_t name, mach_msg_id_t which);
 kern_return_t launchd_mport_create_recv(mach_port_t *name);
 kern_return_t launchd_mport_deallocate(mach_port_t name);
 kern_return_t launchd_mport_make_send(mach_port_t name);
@@ -124,4 +128,6 @@ kern_return_t launchd_mport_copy_send(mach_port_t name);
 kern_return_t launchd_mport_make_send_once(mach_port_t name, mach_port_t *so);
 kern_return_t launchd_mport_close_recv(mach_port_t name);
 
+uint64_t runtime_get_uniqueid(void);
+
 #endif /* __LAUNCHD_RUNTIME_H__ */
index 9e74f83cb476c07f00e779c6f3d862ac9e3b3054..65e08a2a23c28217e35495adb53d375051b98632 100644 (file)
@@ -35,6 +35,7 @@
 #include <IOKit/IOKitLib.h>
 #include <NSSystemDirectories.h>
 #include <mach/mach.h>
+#include <mach-o/getsect.h>
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #include <sys/time.h>
 #include <spawn.h>
 #include <sys/syslimits.h>
 #include <fnmatch.h>
-#include <assumes.h>
+#include <os/assumes.h>
 #include <dlfcn.h>
+#if HAVE_SYSTEMSTATS
+#include <systemstats/systemstats.h>
+#endif
 
 #if HAVE_LIBAUDITD
 #include <bsm/auditd_lib.h>
@@ -94,6 +98,23 @@ extern char **environ;
 #define CFTypeCheck(cf, type) (CFGetTypeID(cf) == type ## GetTypeID())
 #define CFReleaseIfNotNULL(cf) if (cf) CFRelease(cf);
 
+#if TARGET_OS_EMBEDDED
+#include <sys/kern_memorystatus.h>
+
+#define XPC_PLIST_CACHE "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib"
+#define XPC_PLIST_CACHE_KEY "LaunchDaemons"
+
+#if JETSAM_PRIORITY_REVISION
+#define READ_JETSAM_DEFAULTS 1
+#define JETSAM_PROP_DIR "/System/Library/LaunchDaemons"
+#define JETSAM_PROP_DIR_LENGTH (sizeof(JETSAM_PROP_DIR) - 1)
+#define JETSAM_PROP_PREFIX "com.apple.jetsamproperties."
+#define JETSAM_PROP_PREFIX_LENGTH (sizeof(JETSAM_PROP_PREFIX) - 1)
+#define JETSAM_PROP_SUFFIX ".plist"
+#define JETSAM_PROP_SUFFIX_LENGTH (sizeof(JETSAM_PROP_SUFFIX) - 1)
+#endif
+#endif
+
 struct load_unload_state {
        launch_data_t pass1;
        char *session_type;
@@ -103,7 +124,6 @@ struct load_unload_state {
 static void launchctl_log(int level, const char *fmt, ...);
 static void launchctl_log_CFString(int level, CFStringRef string);
 static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context);
-static void job_override(CFTypeRef key, CFTypeRef val, CFMutableDictionaryRef job);
 static CFTypeRef CFTypeCreateFromLaunchData(launch_data_t obj);
 static CFArrayRef CFArrayCreateFromLaunchArray(launch_data_t arr);
 static CFDictionaryRef CFDictionaryCreateFromLaunchDictionary(launch_data_t dict);
@@ -116,6 +136,11 @@ static void sock_dict_cb(launch_data_t what, const char *key, void *context);
 static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob);
 static launch_data_t CF2launch_data(CFTypeRef);
 static launch_data_t read_plist_file(const char *file, bool editondisk, bool load);
+#if TARGET_OS_EMBEDDED
+static CFPropertyListRef GetPropertyListFromCache(void);
+static CFPropertyListRef CreateMyPropertyListFromCachedFile(const char *posixfile);
+static bool require_jobs_from_cache(void);
+#endif
 static CFPropertyListRef CreateMyPropertyListFromFile(const char *);
 static CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL);
 static void WriteMyPropertyListToFile(CFPropertyListRef, const char *);
@@ -256,6 +281,11 @@ static CFMutableDictionaryRef _launchctl_overrides_db = NULL;
 static char *_launchctl_job_overrides_db_path;
 static char *_launchctl_managername = NULL;
 
+#if READ_JETSAM_DEFAULTS
+static CFDictionaryRef _launchctl_jetsam_defaults = NULL;
+static CFDictionaryRef _launchctl_jetsam_defaults_cached = NULL;
+#endif
+
 int
 main(int argc, char *const argv[])
 {
@@ -433,6 +463,7 @@ launchctl_log_CFString(int level, CFStringRef string)
 void
 read_launchd_conf(void)
 {
+#if !TARGET_OS_EMBEDDED
        char s[1000], *c, *av[100];
        const char *file;
        size_t len;
@@ -469,6 +500,7 @@ read_launchd_conf(void)
        }
 
        fclose(f);
+#endif // !TARGET_OS_EMBEDDED
 }
 
 static CFPropertyListRef
@@ -666,24 +698,232 @@ unloadjob(launch_data_t job)
        }
 }
 
-void
-job_override(CFTypeRef key, CFTypeRef val, CFMutableDictionaryRef job)
+#if READ_JETSAM_DEFAULTS
+
+static CFDictionaryRef
+read_jetsam_defaults_from_cache(void) {
+       CFPropertyListRef cache = GetPropertyListFromCache();
+       CFPropertyListRef defaults = NULL;
+       const void **keys = 0;
+       CFIndex count, i;
+
+       if (!cache) {
+               return NULL;
+       }
+        
+       CFPropertyListRef cachefiles = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY));
+       if (!cachefiles) {
+               return NULL;
+       }
+
+       count = CFDictionaryGetCount(cachefiles);
+       keys = (const void **)malloc(sizeof(void *) * count);
+       if (!keys) {
+               return NULL;
+       }
+        
+       CFDictionaryGetKeysAndValues(cachefiles, keys, NULL);
+       for (i = 0; i < count; i++) {
+               CFStringRef key = (CFStringRef)keys[i];
+               CFIndex key_length = CFStringGetLength(key);
+
+               if (key_length <= (CFIndex)(JETSAM_PROP_DIR_LENGTH + JETSAM_PROP_PREFIX_LENGTH + JETSAM_PROP_SUFFIX_LENGTH + 1)) {
+                       continue;
+               }
+
+               if (CFStringCompareWithOptions(key, CFSTR(JETSAM_PROP_DIR "/" JETSAM_PROP_PREFIX), 
+                       CFRangeMake(0, JETSAM_PROP_DIR_LENGTH + JETSAM_PROP_PREFIX_LENGTH + 1), 0)) {
+                       continue;
+               }
+       
+               if (CFStringCompareWithOptions(key, CFSTR(JETSAM_PROP_SUFFIX), 
+                       CFRangeMake(key_length - JETSAM_PROP_SUFFIX_LENGTH, JETSAM_PROP_SUFFIX_LENGTH), 0)) {
+                       continue;
+               }
+
+               defaults = CFDictionaryGetValue(cachefiles, key);
+               break;
+       }
+
+       free(keys);
+
+       return defaults;
+}
+
+static CFDictionaryRef
+read_jetsam_defaults_from_file(void) {
+       DIR *dirp;
+       struct dirent *dp;
+       CFDictionaryRef defaults = NULL;
+
+       dirp = opendir(JETSAM_PROP_DIR);
+       while ((dp = readdir(dirp)) != NULL) {
+               char *fullpath;
+    
+               if (dp->d_namlen <= (JETSAM_PROP_PREFIX_LENGTH + JETSAM_PROP_SUFFIX_LENGTH)) {
+                       continue;
+               }
+       
+               if (strncmp(dp->d_name, JETSAM_PROP_PREFIX, JETSAM_PROP_PREFIX_LENGTH)) {
+                       continue;
+               }
+       
+               if (strncmp(dp->d_name + dp->d_namlen - JETSAM_PROP_SUFFIX_LENGTH, JETSAM_PROP_SUFFIX, JETSAM_PROP_SUFFIX_LENGTH)) {
+                       continue;
+               }
+       
+               if (-1 != asprintf(&fullpath, "%s/%s", JETSAM_PROP_DIR, dp->d_name)) {
+                       defaults = (CFDictionaryRef)CreateMyPropertyListFromFile(fullpath);
+                       free(fullpath);
+               }
+
+               break;
+       }
+
+       if (dirp) {
+               closedir(dirp);
+       }
+        
+       return defaults;    
+}
+
+static bool
+submit_cached_defaults(void) {
+       launch_data_t msg, resp;
+       const void **keys = NULL;
+       int i;
+    
+       if (_launchctl_jetsam_defaults_cached == NULL) {
+        return false;
+       }
+
+       /* The dictionary to transmit */
+       CFMutableDictionaryRef payload_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    
+       /* Add a key to indicate that this is a special job */
+       CFBooleanRef ID = kCFBooleanTrue;
+       CFDictionaryAddValue(payload_dict, CFSTR(LAUNCH_JOBKEY_DEFAULTS), ID);
+  
+       CFMutableDictionaryRef defaults_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+       
+       CFDictionaryAddValue(payload_dict, CFSTR(LAUNCHD_JOB_DEFAULTS), defaults_dict);
+    
+       /* Compile appropriate launchd dictionary... */
+       CFIndex count = CFDictionaryGetCount(_launchctl_jetsam_defaults_cached);
+       keys = (const void **)malloc(sizeof(void *) * count);
+       if (!keys) {
+               goto exit;
+       }
+    
+       CFDictionaryGetKeysAndValues(_launchctl_jetsam_defaults_cached, keys, NULL);
+    
+       for (i = 0; i < count; i++) {
+               CFStringRef label = (CFStringRef)keys[i];
+        
+               /* Get the defaults for the job */
+               CFDictionaryRef job_defaults_dict = CFDictionaryGetValue(_launchctl_jetsam_defaults_cached, label);
+               if (!(job_defaults_dict && CFTypeCheck(job_defaults_dict, CFDictionary))) {
+                       continue;
+               }
+        
+               /* Create a new dictionary to represent the job */
+               CFMutableDictionaryRef job_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+               
+               /* Add the defaults */
+               CFDictionaryAddValue(job_dict, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict);
+        
+               /* Finally, add the result to the main dictionary */
+               CFDictionaryAddValue(defaults_dict, label, job_dict);
+        
+               /* Cleanup */
+               CFRelease(job_dict);
+       }
+    
+       /* Send the payload */
+       launch_data_t ldp = CF2launch_data(payload_dict);
+    
+       msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
+       launch_data_dict_insert(msg, ldp, LAUNCH_KEY_SUBMITJOB);
+
+       resp = launch_msg(msg);
+       launch_data_free(msg);   
+
+       launch_data_free(resp);
+
+exit:
+       CFRelease(defaults_dict);
+       CFRelease(payload_dict);
+       
+       free(keys);
+
+       return true;
+}
+
+static boolean_t
+read_jetsam_defaults(void)
 {
-       if (!CFTypeCheck(key, CFString)) {
-               return;
+       /* Current supported version */
+       const int v = 3;
+    
+       CFDictionaryRef jetsam_defaults = NULL;
+    
+       if (require_jobs_from_cache()) {
+               jetsam_defaults = read_jetsam_defaults_from_cache();
+       } else {
+               jetsam_defaults = read_jetsam_defaults_from_file();
        }
-       if (CFStringCompare(key, CFSTR(LAUNCH_JOBKEY_LABEL), kCFCompareCaseInsensitive) == 0) {
-               return;
+
+       if (NULL == jetsam_defaults) {
+               launchctl_log(LOG_NOTICE, "%s: no jetsam property file found", getprogname());
+               return false;
        }
 
-       CFDictionarySetValue(job, key, val);
+       /* Validate the version */
+       CFNumberRef defaults_vers = CFDictionaryGetValue(jetsam_defaults, CFSTR("Version"));
+       if (!(defaults_vers && CFTypeCheck(defaults_vers, CFNumber))) {
+               return false;
+       }
+       
+       CFNumberRef supported_vers = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &v);
+       if (!(kCFCompareEqualTo == CFNumberCompare(defaults_vers, supported_vers, NULL ))) {
+               return false;
+       }
+
+       /* These defaults are merged within launchctl prior to submitting the job */
+       _launchctl_jetsam_defaults = CFDictionaryGetValue(jetsam_defaults, CFSTR(LAUNCHD_JOB_DEFAULTS));
+       if (!(_launchctl_jetsam_defaults && CFTypeCheck(_launchctl_jetsam_defaults, CFDictionary))) {
+               _launchctl_jetsam_defaults = NULL;
+               return false;
+       }
+
+       /* Cached defaults (applied by launchd) - parse and submit immediately as a fake job */
+       _launchctl_jetsam_defaults_cached = CFDictionaryGetValue(jetsam_defaults, CFSTR(LAUNCHD_JOB_DEFAULTS_CACHED));
+       if (!(_launchctl_jetsam_defaults_cached && CFTypeCheck(_launchctl_jetsam_defaults_cached, CFDictionary))) {
+               _launchctl_jetsam_defaults_cached = NULL;
+               return false;
+       }
+
+       submit_cached_defaults();
+       
+       return true;
 }
 
+#endif /* READ_JETSAM_DEFAULTS */
+
 launch_data_t
 read_plist_file(const char *file, bool editondisk, bool load)
 {
-       CFPropertyListRef plist = CreateMyPropertyListFromFile(file);
+       CFPropertyListRef plist;
        launch_data_t r = NULL;
+#if TARGET_OS_EMBEDDED
+       if (require_jobs_from_cache()) {
+               plist = CreateMyPropertyListFromCachedFile(file);
+       } else {
+               plist = CreateMyPropertyListFromFile(file);
+       }
+#else
+       plist = CreateMyPropertyListFromFile(file);
+#endif
 
        if (NULL == plist) {
                launchctl_log(LOG_ERR, "%s: no plist was returned for: %s", getprogname(), file);
@@ -698,7 +938,10 @@ read_plist_file(const char *file, bool editondisk, bool load)
        if (_launchctl_overrides_db) {
                CFDictionaryRef overrides = CFDictionaryGetValue(_launchctl_overrides_db, label);
                if (overrides && CFTypeCheck(overrides, CFDictionary)) {
-                       CFDictionaryApplyFunction(overrides, (CFDictionaryApplierFunction)job_override, (void *)plist);
+                       CFBooleanRef disabled = CFDictionaryGetValue(overrides, CFSTR(LAUNCH_JOBKEY_DISABLED));
+                       if (disabled && CFTypeCheck(disabled, CFBoolean)) {
+                               CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), disabled);
+                       }
                }
        }
 
@@ -724,6 +967,26 @@ read_plist_file(const char *file, bool editondisk, bool load)
                }
        }
 
+#if READ_JETSAM_DEFAULTS
+       if (_launchctl_jetsam_defaults) {
+               CFDictionaryRef job_defaults_dict = CFDictionaryGetValue(_launchctl_jetsam_defaults, label);
+               if (job_defaults_dict) {
+                       CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict);
+               }
+       } else {
+               /* The plist is missing. Set a default memory limit, since the device will be otherwise unusable */
+               long default_limit = 0;
+               CFMutableDictionaryRef job_defaults_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+               CFNumberRef memory_limit = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &default_limit);
+               if (memory_limit) {
+                       CFDictionaryAddValue(job_defaults_dict, CFSTR(LAUNCH_JOBKEY_JETSAMMEMORYLIMIT), memory_limit);
+                       CFRelease(memory_limit);
+               }
+               CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict);
+               CFRelease(job_defaults_dict);
+       }
+#endif /* READ_JETSAM_DEFAULTS */
+
        r = CF2launch_data(plist);
 
        CFRelease(plist);
@@ -757,7 +1020,7 @@ limitloadtohardware_iterator(launch_data_t val, const char *key, void *ctx)
 
        int mib[2];
        size_t sz = 2;
-       if (*result != true && osx_assumes_zero(sysctlnametomib(name, mib, &sz)) == 0) {
+       if (*result != true && os_assumes_zero(sysctlnametomib(name, mib, &sz)) == 0) {
                if (launch_data_get_type(val) == LAUNCH_DATA_ARRAY) {
                        size_t c = launch_data_array_get_count(val);
 
@@ -1426,6 +1689,115 @@ do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgrou
        freeaddrinfo(res0);
 }
 
+#pragma mark XPC Cache
+
+#if TARGET_OS_EMBEDDED
+
+CFPropertyListRef
+GetPropertyListFromCache(void)
+{
+       static CFPropertyListRef propertyList;
+       CFDataRef cacheData;
+       CFErrorRef error;
+
+       if (!propertyList) {
+               uint8_t *data = NULL;
+               unsigned long sz = 0;
+
+               void *handle = dlopen(XPC_PLIST_CACHE, RTLD_NOW);
+
+               if (handle) {
+                       void *fnptr = dlsym(handle, "__xpcd_cache");
+
+                       if (fnptr) {
+                               Dl_info image_info;
+
+                               int rv = dladdr(fnptr, &image_info);
+                               if (rv != 0) {
+                                       data = getsectiondata(image_info.dli_fbase, "__TEXT", "__xpcd_cache", &sz);
+                               } else {
+                                       launchctl_log(LOG_ERR, "cache loading failed: failed to find address of __xpcd_cache symbol.");
+                               }
+                       } else {
+                               launchctl_log(LOG_ERR, "cache loading failed: failed to find __xpcd_cache symbol in cache.");
+                       }
+               } else {
+                       launchctl_log(LOG_ERR, "cache loading failed: dlopen returned %s.", dlerror());
+               }
+
+               if (data) {
+                       cacheData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, data, sz, kCFAllocatorNull);
+                       if (cacheData) {
+                               propertyList = CFPropertyListCreateWithData(kCFAllocatorDefault, cacheData, kCFPropertyListMutableContainersAndLeaves, NULL, &error);
+                               CFRelease(cacheData);
+                       } else {
+                               launchctl_log(LOG_ERR, "cache loading failed: unable to create data out of memory region.");
+                       }
+               } else {
+                       launchctl_log(LOG_ERR, "cache loading failed: no cache data found in __TEXT,__xpcd_cache segment.");
+               }
+       }
+
+       return propertyList;
+}
+
+CFPropertyListRef
+CreateMyPropertyListFromCachedFile(const char *posixfile)
+{
+       CFPropertyListRef cache = GetPropertyListFromCache();
+       CFPropertyListRef job = NULL;
+
+       if (cache) {
+               CFPropertyListRef jobs = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY));
+
+               if (jobs) {
+                       CFStringRef key = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, posixfile, kCFStringEncodingUTF8, kCFAllocatorNull);
+
+                       if (key) {
+                               job = CFDictionaryGetValue(jobs, key);
+                               CFRelease(key);
+                       }
+               }
+       }
+
+       if (job) {
+               CFRetain(job);
+       }
+       return job;
+}
+
+bool
+require_jobs_from_cache(void)
+{
+       char buf[1024];
+       size_t len;
+       char *ptr;
+       unsigned long val;
+       bool cs_disabled = false;
+       len = sizeof(buf);
+
+       if (sysctlbyname("kern.bootargs", buf, &len, NULL, 0) == 0) {
+               ptr = strnstr(buf, "cs_enforcement_disable=", len);
+               if (ptr != NULL) {
+                       val = strtoul(ptr + strlen("cs_enforcement_disable="), NULL, 10);
+                       cs_disabled = (val != 0);
+               }
+               ptr = strnstr(buf, "launchctl_enforce_codesign=", len);
+               if (ptr != NULL) {
+                       char *endptr = NULL;
+                       char *startptr = ptr + strlen("launchctl_enforce_codesign=");
+                       val = strtoul(startptr, &endptr, 10);
+                       cs_disabled = (val == 0 && startptr != endptr);
+               }
+       }
+
+       return !cs_disabled;
+}
+
+#endif
+
+#pragma mark File-based Property Lists
+
 CFPropertyListRef
 CreateMyPropertyListFromFile(const char *posixfile)
 {
@@ -1756,7 +2128,7 @@ do_single_user_mode2(void)
        case 0:
                break;
        default:
-               (void)osx_assumes_zero(waitpid(p, &wstatus, 0));
+               (void)os_assumes_zero(waitpid(p, &wstatus, 0));
                if (WIFEXITED(wstatus)) {
                        if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
                                return true;
@@ -1818,7 +2190,7 @@ do_crash_debug_mode2(void)
        case 0:
                break;
        default:
-               (void)osx_assumes_zero(waitpid(p, &wstatus, 0));
+               (void)os_assumes_zero(waitpid(p, &wstatus, 0));
                if (WIFEXITED(wstatus)) {
                        if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
                                return true;
@@ -1893,6 +2265,22 @@ handle_system_bootstrapper_crashes_separately(void)
        (void)posix_assumes_zero(sigaction(SIGABRT, &fsa, NULL));
 }
 
+#if TARGET_OS_EMBEDDED
+static void
+init_data_protection(void)
+{
+       if (path_check("/usr/libexec/init_data_protection")) {
+               const char *init_cp[] = { "/usr/libexec/init_data_protection", NULL };
+               if (fwexec(init_cp, NULL) == -1) {
+                       launchctl_log(LOG_ERR, "Couldn't init content protection: %d: %s", errno, strerror(errno));
+                       (void)reboot(RB_HALT);
+
+                       _exit(EXIT_FAILURE);
+               }
+       }
+}
+#endif
+
 static void
 system_specific_bootstrap(bool sflag)
 {
@@ -1926,7 +2314,7 @@ system_specific_bootstrap(bool sflag)
        EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 60, 0);
        (void)posix_assumes_zero(kevent(kq, &kev, 1, NULL, 0, NULL));
 
-       __OSX_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1);
+       __OS_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1);
        EV_SET(&kev, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, 0);
        (void)posix_assumes_zero(kevent(kq, &kev, 1, NULL, 0, NULL));
        (void)posix_assumes_zero(signal(SIGTERM, SIG_IGN));
@@ -1969,6 +2357,13 @@ system_specific_bootstrap(bool sflag)
                do_potential_fsck();
        }
 
+#if TARGET_OS_EMBEDDED
+       if (path_check("/usr/libexec/tzinit")) {
+               const char *tzinit_tool[] = { "/usr/libexec/tzinit", NULL };
+               (void)posix_assumes_zero(fwexec(tzinit_tool, NULL));
+       }
+#endif
+
 #if TARGET_OS_EMBEDDED
        if (path_check("/usr/libexec/FinishRestoreFromBackup")) {
                const char *finish_restore[] = { "/usr/libexec/FinishRestoreFromBackup", NULL };
@@ -2051,7 +2446,7 @@ system_specific_bootstrap(bool sflag)
 #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)) {
-               (void)osx_assumes_zero(audit_quick_start());
+               (void)os_assumes_zero(audit_quick_start());
                launch_data_free(lda);  
        }
 #else
@@ -2061,6 +2456,10 @@ system_specific_bootstrap(bool sflag)
        }
 #endif
 
+#if HAVE_SYSTEMSTATS
+       systemstats_boot();
+#endif
+
        do_BootCache_magic(BOOTCACHE_START);
 
        preheat_page_cache_hack();
@@ -2311,25 +2710,59 @@ load_and_unload_cmd(int argc, char *const argv[])
                }
        }
 
+#if READ_JETSAM_DEFAULTS
+       if (!read_jetsam_defaults()) {
+               launchctl_log(LOG_NOTICE, "Failed to read jetsam defaults; no process limits applied");     
+       }
+#endif  
+
        /* Only one pass! */
        lus.pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY);
 
        es = NSStartSearchPathEnumeration(NSLibraryDirectory, es);
 
        while ((es = NSGetNextSearchPathEnumeration(es, nspath))) {
-               glob_t g;
-
                if (lus.session_type) {
                        strcat(nspath, "/LaunchAgents");
                } else {
                        strcat(nspath, "/LaunchDaemons");
                }
 
-               if (glob(nspath, GLOB_TILDE|GLOB_NOSORT, NULL, &g) == 0) {
-                       for (i = 0; i < g.gl_pathc; i++) {
-                               readpath(g.gl_pathv[i], &lus);
+               bool should_glob = true;
+
+#if TARGET_OS_EMBEDDED
+               if (require_jobs_from_cache()) {
+                       CFDictionaryRef cache = GetPropertyListFromCache();
+                       if (cache) {
+                               CFDictionaryRef launchdJobs = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY));
+                               if (launchdJobs) {
+                                       CFIndex sz = CFDictionaryGetCount(launchdJobs);
+
+                                       CFStringRef *keys = malloc(sz * sizeof(CFStringRef));
+                                       CFDictionaryGetKeysAndValues(launchdJobs, (const void**)keys, NULL);
+
+                                       for (i=0; i < (size_t)sz; i++) {
+                                               char path[PATH_MAX];
+                                               if (CFStringGetCString(keys[i], path, PATH_MAX, kCFStringEncodingUTF8) && (strncmp(path, nspath, strlen(nspath)) == 0)) {
+                                                       readpath(path, &lus);
+                                               }
+                                       }
+                               }
+                       }
+
+                       should_glob = false;
+               }
+#endif
+
+               if (should_glob) {
+                       glob_t g;
+
+                       if (glob(nspath, GLOB_TILDE|GLOB_NOSORT, NULL, &g) == 0) {
+                               for (i = 0; i < g.gl_pathc; i++) {
+                                       readpath(g.gl_pathv[i], &lus);
+                               }
+                               globfree(&g);
                        }
-                       globfree(&g);
                }
        }
 
@@ -3522,7 +3955,7 @@ loopback_setup_ipv6(void)
        ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
 
        if (ioctl(s6, SIOCAIFADDR_IN6, &ifra6) == -1 && errno != EEXIST) {
-               (void)osx_assumes_zero(errno);
+               (void)os_assumes_zero(errno);
        }
 
        (void)close(s6);
@@ -3620,6 +4053,15 @@ do_potential_fsck(void)
 
        return;
 out:
+
+#if TARGET_OS_EMBEDDED
+       /* Once we've validated the root filesystem, kick off any
+        * tasks needed for data protection before we mount other file
+        * systems.
+        */
+       init_data_protection();
+#endif
+
        /* 
         * Once this is fixed:
         *
@@ -3648,6 +4090,8 @@ out:
 void
 fix_bogus_file_metadata(void)
 {
+       // Don't do any of this on embedded: <rdar://problem/13212363>
+#if !TARGET_OS_EMBEDDED
        static const struct {
                const char *path;
                const uid_t owner;
@@ -3664,11 +4108,9 @@ fix_bogus_file_metadata(void)
                { LAUNCHD_DB_PREFIX "/com.apple.launchd", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH, true },
                // Fixing <rdar://problem/7571633>.
                { _PATH_VARDB, 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true },
-#if !TARGET_OS_EMBEDDED
                { _PATH_VARDB "mds/", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true },
                // Similar fix for <rdar://problem/6550172>.
                { "/Library/StartupItems", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true },
-#endif
        };
        struct stat sb;
        size_t i;
@@ -3719,6 +4161,7 @@ fix_bogus_file_metadata(void)
                        (void)posix_assumes_zero(chown(f[i].path, f[i].owner, f[i].group));
                }
        }
+#endif
 }
 
 
@@ -3784,7 +4227,7 @@ empty_dir(const char *thedir, struct stat *psb)
        }
 
        if (!(od = opendir("."))) {
-               (void)osx_assumes_zero(errno);
+               (void)os_assumes_zero(errno);
                goto out;
        }
 
@@ -3813,7 +4256,7 @@ empty_dir(const char *thedir, struct stat *psb)
                                continue;
                        }
 
-                       if (osx_assumes(psb->st_dev == sb.st_dev)) {
+                       if (os_assumes(psb->st_dev == sb.st_dev)) {
                                continue;
                        }
                }
@@ -3860,7 +4303,7 @@ apply_sysctls_from_file(const char *thefile)
                        continue;
                }
                if (!(tmpstr = malloc(ln_len + 1))) {
-                       (void)osx_assumes_zero(errno);
+                       (void)os_assumes_zero(errno);
                        continue;
                }
                memcpy(tmpstr, val, ln_len);
@@ -4002,7 +4445,7 @@ do_application_firewall_magic(int sfd, launch_data_t thejob)
                 * errno == ENOPROTOOPT.
                 */
                if (setsockopt(sfd, SOL_SOCKET, SO_EXECPATH, prog, (socklen_t)(strlen(prog) + 1)) == -1 && errno != ENOPROTOOPT) {
-                       (void)osx_assumes_zero(errno);
+                       (void)os_assumes_zero(errno);
                }
        }
 }
@@ -4038,7 +4481,7 @@ preheat_page_cache_hack(void)
                        if ((sb.st_size < 10*1024*1024) && (junkbuf = malloc((size_t)sb.st_size)) != NULL) {
                                ssize_t n = read(fd, junkbuf, (size_t)sb.st_size);
                                if (posix_assumes_zero(n) != -1 && n != (ssize_t)sb.st_size) {
-                                       (void)osx_assumes_zero(n);
+                                       (void)os_assumes_zero(n);
                                }
                                free(junkbuf);
                        }
@@ -4061,7 +4504,7 @@ do_bootroot_magic(void)
 
        chosen = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/chosen");
 
-       if (!osx_assumes(chosen)) {
+       if (!os_assumes(chosen)) {
                return;
        }
 
index d7c44a940d650c1afe24791ec09035ddd197d399..906a7db611d7af34b968beea1b167b832f3ed5e5 100644 (file)
@@ -2,5 +2,3 @@
 
 INSTALL_PATH = /bin
 PRODUCT_NAME = launchctl
-// launchctl doesn't need to redirect osx_assumes() output.
-OTHER_LDFLAGS = 
index 809025ef44b1a1bd80db85cd5967b93cdbc3b408..a2df031a64d8d8c11e747cd0afa9f8b78e804602 100644 (file)
@@ -14,4 +14,4 @@ ALWAYS_SEARCH_USER_PATHS = NO
 GCC_ENABLE_BUILTIN_FUNCTIONS = YES
 OTHER_CFLAGS = $(OTHER_CFLAGS) -DXPC_BUILDING_LAUNCHD=1
 OTHER_MIGFLAGS = -DXPC_BUILDING_LAUNCHD=1 -I$(PROJECT_DIR)/src -I$(SDKROOT)/usr/local/include
-OTHER_LDFLAGS = -sectcreate __TEXT __osx_log_func $(BUILD_XCSUPPORT_DIR)/osx_redirect_name
+OTHER_LDFLAGS =
index ce2193fc5ed164456c81a996c8ee3455292a2143..aafdd2234dc54bb003638948b02156650f21749a 100644 (file)
@@ -13,7 +13,9 @@ DEPLOYMENT_LOCATION = YES
 INSTALL_PATH = /usr/lib/system
 ORDER_FILE[sdk=macosx*] = $(SDKROOT)/$(APPLE_INTERNAL_DIR)/OrderFiles/liblaunch.order
 ORDER_FILE[sdk=iphonesimulator*] = 
-OTHER_LDFLAGS = -umbrella System $(CRASHREPORTER_LINKER_FLAGS)
+LINK_WITH_STANDARD_LIBRARIES = NO
+OTHER_LDFLAGS = -umbrella System -L/usr/lib/system -ldyld -lcompiler_rt -lsystem_kernel -lsystem_platform -lsystem_pthread -lsystem_malloc -lsystem_c -lquarantine -ldispatch $(CRASHREPORTER_LINKER_FLAGS)
+OTHER_LDFLAGS[sdk=iphone*] = -umbrella System -L/usr/lib/system -ldyld -lcompiler_rt -lsystem_kernel -lsystem_platform -lsystem_pthread -lsystem_malloc -lsystem_c -ldispatch $(CRASHREPORTER_LINKER_FLAGS)
 LD_DYLIB_INSTALL_NAME = $(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)
 MACH_O_TYPE = mh_dylib
 EXECUTABLE_PREFIX = lib
diff --git a/xcsupport/osx_redirect_name b/xcsupport/osx_redirect_name
deleted file mode 100644 (file)
index 2e3e59b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-_launchd_osx_redirect
\ No newline at end of file