From: Apple Date: Tue, 29 Oct 2013 00:03:35 +0000 (+0000) Subject: launchd-842.1.4.tar.gz X-Git-Tag: os-x-109^0 X-Git-Url: https://git.saurik.com/apple/launchd.git/commitdiff_plain/953793947dbaa450ae0bfb5b464e83882d3877bb?ds=inline launchd-842.1.4.tar.gz --- diff --git a/launchd.xcodeproj/project.pbxproj b/launchd.xcodeproj/project.pbxproj index c424676..408a2a2 100644 --- a/launchd.xcodeproj/project.pbxproj +++ b/launchd.xcodeproj/project.pbxproj @@ -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 */; }; @@ -395,7 +394,6 @@ 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 = ""; }; 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 = ""; }; 7215DE4B0EFAF2EC00ABD81E /* libauditd.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libauditd.dylib; path = /usr/lib/libauditd.dylib; sourceTree = ""; }; 721FBEA50EA7ABC40057462B /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = config.h; path = src/config.h; sourceTree = ""; }; 72FDB15D0EA7D7B200B2AC84 /* ktrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ktrace.c; path = src/ktrace.c; sourceTree = ""; }; @@ -655,7 +653,6 @@ 4BC868A6143CFC8C00B46F40 /* xcsupport */ = { isa = PBXGroup; children = ( - 4BC868A8143CFD0300B46F40 /* osx_redirect_name */, ); name = xcsupport; sourceTree = ""; @@ -916,7 +913,7 @@ FC59A03F0E8C87FD00D41150 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0450; + LastUpgradeCheck = 0500; }; buildConfigurationList = FC59A0420E8C87FD00D41150 /* Build configuration list for PBXProject "launchd" */; compatibilityVersion = "Xcode 3.2"; @@ -1099,7 +1096,6 @@ FC59A0F40E8C8AA600D41150 /* liblaunch.c in Sources */, FC59A0F00E8C8AA600D41150 /* libvproc.c in Sources */, FC59A0F70E8C8AA600D41150 /* libbootstrap.c in Sources */, - 726056090EA7FCF200D65FE7 /* ktrace.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1221,7 +1217,6 @@ FC59A0410E8C87FD00D41150 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = YES; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; diff --git a/liblaunch/launch.h b/liblaunch/launch.h index aadc62f..07ea8b7 100644 --- a/liblaunch/launch.h +++ b/liblaunch/launch.h @@ -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" diff --git a/liblaunch/launch_internal.h b/liblaunch/launch_internal.h index aa6576f..a78d4bd 100644 --- a/liblaunch/launch_internal.h +++ b/liblaunch/launch_internal.h @@ -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@ * @@ -21,14 +21,69 @@ #ifndef __LAUNCH_INTERNAL_H__ #define __LAUNCH_INTERNAL_H__ +#include "vproc_priv.h" + #include +#include +#include + +#if __has_include() +#include +#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); diff --git a/liblaunch/launch_priv.h b/liblaunch/launch_priv.h index 878101b..518cf95 100644 --- a/liblaunch/launch_priv.h +++ b/liblaunch/launch_priv.h @@ -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. diff --git a/liblaunch/libbootstrap.c b/liblaunch/libbootstrap.c index c9f85ad..2516649 100644 --- a/liblaunch/libbootstrap.c +++ b/liblaunch/libbootstrap.c @@ -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); } diff --git a/liblaunch/liblaunch.c b/liblaunch/liblaunch.c index 0afdf0a..6cef3dd 100644 --- a/liblaunch/liblaunch.c +++ b/liblaunch/liblaunch.c @@ -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; } diff --git a/liblaunch/libvproc.c b/liblaunch/libvproc.c index a9151de..7e035bd 100644 --- a/liblaunch/libvproc.c +++ b/liblaunch/libvproc.c @@ -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 #include #include -#include +#include #if HAVE_QUARANTINE #include @@ -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) diff --git a/liblaunch/vproc_priv.h b/liblaunch/vproc_priv.h index 954b481..8d3b0e5 100644 --- a/liblaunch/vproc_priv.h +++ b/liblaunch/vproc_priv.h @@ -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 #include #include +#include #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); diff --git a/man/launchd.plist.5 b/man/launchd.plist.5 index 068f56a..f7f5a09 100644 --- a/man/launchd.plist.5 +++ b/man/launchd.plist.5 @@ -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 +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 When a job dies, .Nm launchd diff --git a/src/config.h b/src/config.h index 841f524..3084872 100644 --- a/src/config.h +++ b/src/config.h @@ -9,6 +9,12 @@ #define HAVE_QUARANTINE 0 #endif +#if __has_include() +#define HAVE_RESPONSIBILITY 1 +#else +#define HAVE_RESPONSIBILITY 0 +#endif + #if __has_include() #define HAVE_SANDBOX 1 #else @@ -17,4 +23,10 @@ #define HAVE_LIBAUDITD !TARGET_OS_EMBEDDED +#if !TARGET_OS_EMBEDDED && __has_include() +#define HAVE_SYSTEMSTATS 1 +#else +#define HAVE_SYSTEMSTATS 0 +#endif + #endif /* __CONFIG_H__ */ diff --git a/src/core.c b/src/core.c index beb8e21..e5720e4 100644 --- a/src/core.c +++ b/src/core.c @@ -82,14 +82,16 @@ #include #include #include -#include +#include #include +#include +#include <_simple.h> #include +#include #include #include #include -#include #if HAVE_SANDBOX #define __APPLE_API_PRIVATE #include @@ -97,9 +99,15 @@ #if HAVE_QUARANTINE #include #endif +#if HAVE_RESPONSIBILITY +#include +#endif #if !TARGET_OS_EMBEDDED extern int gL1CacheEnabled; #endif +#if HAVE_SYSTEMSTATS +#include +#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 . */ +#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. + // + // + 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 . + * + * Also ensure that daemons have a default memory highwatermark unless + * otherwise specified, as per . */ 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 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 */ + 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 . + * See and . */ - 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. + * + * + */ + 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: "); 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. + // + // + (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 . - */ - (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. + * * . */ *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 +} diff --git a/src/core.h b/src/core.h index 0bcda03..b8267c5 100644 --- a/src/core.h +++ b/src/core.h @@ -24,6 +24,7 @@ #include "runtime.h" #include "bootstrap.h" #include "launch.h" +#include 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__ */ diff --git a/src/ipc.c b/src/ipc.c index d6a1d08..7d902bd 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -45,7 +45,7 @@ #include #include #include -#include +#include #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 diff --git a/src/job.defs b/src/job.defs index cf40b95..81b536e 100644 --- a/src/job.defs +++ b/src/job.defs @@ -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 +); diff --git a/src/job_types.defs b/src/job_types.defs index f6ba31a..77390a0 100644 --- a/src/job_types.defs +++ b/src/job_types.defs @@ -23,9 +23,11 @@ * 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; diff --git a/src/launchd.c b/src/launchd.c index e9fd0e6..0f8151d 100644 --- a/src/launchd.c +++ b/src/launchd.c @@ -64,7 +64,7 @@ #include #include #include -#include +#include #if HAVE_LIBAUDITD #include @@ -221,8 +221,8 @@ main(int argc, char *const *argv) * */ 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; } diff --git a/src/log.c b/src/log.c index d74e418..1bd8b49 100644 --- a/src/log.c +++ b/src/log.c @@ -1,5 +1,5 @@ #include -#include +#include #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; } diff --git a/src/log.h b/src/log.h index 4afc53d..517f181 100644 --- 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); diff --git a/src/runtime.c b/src/runtime.c index b65b7d1..026f0eb 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include #include @@ -58,7 +60,7 @@ #include #include #include -#include +#include #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 } diff --git a/src/runtime.h b/src/runtime.h index 2b8e985..a13deff 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -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__ */ diff --git a/support/launchctl.c b/support/launchctl.c index 9e74f83..65e08a2 100644 --- a/support/launchctl.c +++ b/support/launchctl.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -75,8 +76,11 @@ #include #include #include -#include +#include #include +#if HAVE_SYSTEMSTATS +#include +#endif #if HAVE_LIBAUDITD #include @@ -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 + +#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: +#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 . { _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 . { "/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; } diff --git a/xcconfigs/launchctl.xcconfig b/xcconfigs/launchctl.xcconfig index d7c44a9..906a7db 100644 --- a/xcconfigs/launchctl.xcconfig +++ b/xcconfigs/launchctl.xcconfig @@ -2,5 +2,3 @@ INSTALL_PATH = /bin PRODUCT_NAME = launchctl -// launchctl doesn't need to redirect osx_assumes() output. -OTHER_LDFLAGS = diff --git a/xcconfigs/launchd.xcconfig b/xcconfigs/launchd.xcconfig index 809025e..a2df031 100644 --- a/xcconfigs/launchd.xcconfig +++ b/xcconfigs/launchd.xcconfig @@ -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 = diff --git a/xcconfigs/liblaunch.xcconfig b/xcconfigs/liblaunch.xcconfig index ce2193f..aafdd22 100644 --- a/xcconfigs/liblaunch.xcconfig +++ b/xcconfigs/liblaunch.xcconfig @@ -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 index 2e3e59b..0000000 --- a/xcsupport/osx_redirect_name +++ /dev/null @@ -1 +0,0 @@ -_launchd_osx_redirect \ No newline at end of file