From eabd170121c913d6b497fa2503e49f09f5412ddc Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 27 Jul 2012 18:02:04 +0000 Subject: [PATCH] launchd-442.21.tar.gz --- {launchd/src => SystemStarter}/IPC.c | 0 {launchd/src => SystemStarter}/IPC.h | 0 .../src => SystemStarter}/StartupItemContext | 0 .../StartupItemContext.8 | 0 {launchd/src => SystemStarter}/StartupItems.c | 0 {launchd/src => SystemStarter}/StartupItems.h | 0 .../src => SystemStarter}/SystemStarter.8 | 0 .../src => SystemStarter}/SystemStarter.c | 0 .../src => SystemStarter}/SystemStarter.h | 0 .../src => SystemStarter}/SystemStarterIPC.h | 0 .../com.apple.SystemStarter.plist | 0 {launchd/src => SystemStarter}/hostconfig | 0 launchd.xcodeproj/project.pbxproj | 603 +- launchd/AUTHORS | 4 - launchd/COPYING | 202 - launchd/doc/HOWTO.html | 270 - launchd/doc/StartupItem-NOTES.rtf | 96 - launchd/doc/sampled.c | 123 - launchd/doc/sampled.plist | 40 - launchd/src/config.h | 7 - launchd/src/launch.h | 243 - launchd/src/launch_priv.h | 152 - launchd/src/launchd_runtime.h | 197 - launchd/testing/EVFILT_TIMER.c | 50 - launchd/testing/StartCalendarInterval.plist | 16 - launchd/testing/StartInterval.plist | 15 - launchd/testing/badexec.plist | 14 - launchd/testing/badexit.plist | 14 - launchd/testing/loginwindow_helper_code.c | 38 - launchd/testing/mach_via_launchd.c | 47 - launchd/testing/mach_via_launchd.plist | 19 - launchd/testing/missed-EVFILT_READ.c | 112 - launchd/testing/missed-EVFILT_TIMER.c | 58 - launchd/testing/missed-EVFILT_WRITE.c | 156 - launchd/testing/missed-fds.c | 186 - launchd/testing/missing_req_keys.plist | 6 - launchd/testing/our_kill_is_busted.c | 56 - launchd/testing/secsock.plist | 21 - launchd/testing/signaldeath.plist | 16 - launchd/testing/spawn_via_launchd.c | 53 - launchd/testing/vproc_gsk_test.c | 27 - launchd/testing/vproc_swap_complex.c | 28 - {launchd/src => liblaunch}/bootstrap.h | 6 +- {launchd/src => liblaunch}/bootstrap_priv.h | 7 +- liblaunch/launch.h | 341 ++ {launchd/src => liblaunch}/launch_internal.h | 37 +- liblaunch/launch_priv.h | 160 + {launchd/src => liblaunch}/launchd.ops | 0 {launchd/src => liblaunch}/libbootstrap.c | 14 +- {launchd/src => liblaunch}/liblaunch.c | 122 +- {launchd/src => liblaunch}/libvproc.c | 689 +-- {launchd/src => liblaunch}/reboot2.h | 10 +- {launchd/src => liblaunch}/vproc.h | 7 +- {launchd/src => liblaunch}/vproc_internal.h | 46 +- {launchd/src => liblaunch}/vproc_priv.h | 142 +- {launchd/src => man}/launchctl.1 | 0 {launchd/src => man}/launchd.8 | 0 {launchd/src => man}/launchd.conf.5 | 0 {launchd/src => man}/launchd.plist.5 | 0 {launchd/src => man}/launchproxy.8 | 0 {launchd/src => man}/rc.8 | 0 {launchd/src => man}/wait4path.1 | 0 {launchd/src => rc}/rc.common | 0 {launchd/src => rc}/rc.netboot | 0 src/config.h | 20 + .../src/launchd_core_logic.c => src/core.c | 5079 +++++++++-------- .../src/launchd_core_logic.h => src/core.h | 23 +- .../launchd_helper.defs => src/helper.defs | 7 +- .../internal.defs | 2 +- launchd/src/launchd_unix_ipc.c => src/ipc.c | 97 +- launchd/src/launchd_unix_ipc.h => src/ipc.h | 12 +- .../src/protocol_vproc.defs => src/job.defs | 52 +- .../job_forward.defs | 2 +- .../job_reply.defs | 18 +- .../job_types.defs | 41 +- .../src/launchd_runtime_kill.c => src/kill2.c | 8 +- .../src/launchd_runtime_kill.h => src/kill2.h | 10 +- launchd/src/launchd_ktrace.c => src/ktrace.c | 8 +- launchd/src/launchd_ktrace.h => src/ktrace.h | 6 +- {launchd/src => src}/launchd.c | 397 +- {launchd/src => src}/launchd.h | 29 +- src/log.c | 398 ++ src/log.h | 71 + {launchd/src => src}/protocol_jobmgr.defs | 0 .../src/launchd_runtime.c => src/runtime.c | 1044 +--- src/runtime.h | 127 + {launchd/src => support}/launchctl.c | 1773 +++--- {launchd/src => support}/launchproxy.c | 0 {launchd/src => support}/wait4path.c | 2 +- xcconfigs/common.xcconfig | 27 + xcconfigs/launchctl.xcconfig | 6 + xcconfigs/launchd.xcconfig | 17 + xcconfigs/liblaunch.xcconfig | 30 + xcscripts/SystemStarter-postflight.sh | 4 + xcscripts/launchctl-postflight.sh | 6 + xcscripts/launchd-postflight.sh | 16 + xcscripts/liblaunch-postflight.sh | 5 + xcsupport/osx_redirect_name | 1 + 98 files changed, 6102 insertions(+), 7686 deletions(-) rename {launchd/src => SystemStarter}/IPC.c (100%) rename {launchd/src => SystemStarter}/IPC.h (100%) rename {launchd/src => SystemStarter}/StartupItemContext (100%) rename {launchd/src => SystemStarter}/StartupItemContext.8 (100%) rename {launchd/src => SystemStarter}/StartupItems.c (100%) rename {launchd/src => SystemStarter}/StartupItems.h (100%) rename {launchd/src => SystemStarter}/SystemStarter.8 (100%) rename {launchd/src => SystemStarter}/SystemStarter.c (100%) rename {launchd/src => SystemStarter}/SystemStarter.h (100%) rename {launchd/src => SystemStarter}/SystemStarterIPC.h (100%) rename {launchd/src => SystemStarter}/com.apple.SystemStarter.plist (100%) rename {launchd/src => SystemStarter}/hostconfig (100%) delete mode 100644 launchd/AUTHORS delete mode 100644 launchd/COPYING delete mode 100644 launchd/doc/HOWTO.html delete mode 100644 launchd/doc/StartupItem-NOTES.rtf delete mode 100644 launchd/doc/sampled.c delete mode 100644 launchd/doc/sampled.plist delete mode 100644 launchd/src/config.h delete mode 100644 launchd/src/launch.h delete mode 100644 launchd/src/launch_priv.h delete mode 100644 launchd/src/launchd_runtime.h delete mode 100644 launchd/testing/EVFILT_TIMER.c delete mode 100644 launchd/testing/StartCalendarInterval.plist delete mode 100644 launchd/testing/StartInterval.plist delete mode 100644 launchd/testing/badexec.plist delete mode 100644 launchd/testing/badexit.plist delete mode 100644 launchd/testing/loginwindow_helper_code.c delete mode 100644 launchd/testing/mach_via_launchd.c delete mode 100644 launchd/testing/mach_via_launchd.plist delete mode 100644 launchd/testing/missed-EVFILT_READ.c delete mode 100644 launchd/testing/missed-EVFILT_TIMER.c delete mode 100644 launchd/testing/missed-EVFILT_WRITE.c delete mode 100644 launchd/testing/missed-fds.c delete mode 100644 launchd/testing/missing_req_keys.plist delete mode 100644 launchd/testing/our_kill_is_busted.c delete mode 100644 launchd/testing/secsock.plist delete mode 100644 launchd/testing/signaldeath.plist delete mode 100644 launchd/testing/spawn_via_launchd.c delete mode 100644 launchd/testing/vproc_gsk_test.c delete mode 100644 launchd/testing/vproc_swap_complex.c rename {launchd/src => liblaunch}/bootstrap.h (99%) rename {launchd/src => liblaunch}/bootstrap_priv.h (96%) create mode 100644 liblaunch/launch.h rename {launchd/src => liblaunch}/launch_internal.h (69%) create mode 100644 liblaunch/launch_priv.h rename {launchd/src => liblaunch}/launchd.ops (100%) rename {launchd/src => liblaunch}/libbootstrap.c (99%) rename {launchd/src => liblaunch}/liblaunch.c (96%) rename {launchd/src => liblaunch}/libvproc.c (64%) rename {launchd/src => liblaunch}/reboot2.h (86%) rename {launchd/src => liblaunch}/vproc.h (98%) rename {launchd/src => liblaunch}/vproc_internal.h (74%) rename {launchd/src => liblaunch}/vproc_priv.h (52%) rename {launchd/src => man}/launchctl.1 (100%) rename {launchd/src => man}/launchd.8 (100%) rename {launchd/src => man}/launchd.conf.5 (100%) rename {launchd/src => man}/launchd.plist.5 (100%) rename {launchd/src => man}/launchproxy.8 (100%) rename {launchd/src => man}/rc.8 (100%) rename {launchd/src => man}/wait4path.1 (100%) rename {launchd/src => rc}/rc.common (100%) rename {launchd/src => rc}/rc.netboot (100%) create mode 100644 src/config.h rename launchd/src/launchd_core_logic.c => src/core.c (70%) rename launchd/src/launchd_core_logic.h => src/core.h (84%) rename launchd/src/launchd_helper.defs => src/helper.defs (73%) rename launchd/src/launchd_internal.defs => src/internal.defs (96%) rename launchd/src/launchd_unix_ipc.c => src/ipc.c (83%) rename launchd/src/launchd_unix_ipc.h => src/ipc.h (90%) rename launchd/src/protocol_vproc.defs => src/job.defs (88%) rename launchd/src/protocol_job_forward.defs => src/job_forward.defs (97%) rename launchd/src/protocol_job_reply.defs => src/job_reply.defs (91%) rename launchd/src/launchd_mig_types.defs => src/job_types.defs (51%) rename launchd/src/launchd_runtime_kill.c => src/kill2.c (88%) rename launchd/src/launchd_runtime_kill.h => src/kill2.h (82%) rename launchd/src/launchd_ktrace.c => src/ktrace.c (84%) rename launchd/src/launchd_ktrace.h => src/ktrace.h (93%) rename {launchd/src => src}/launchd.c (59%) rename {launchd/src => src}/launchd.h (63%) create mode 100644 src/log.c create mode 100644 src/log.h rename {launchd/src => src}/protocol_jobmgr.defs (100%) rename launchd/src/launchd_runtime.c => src/runtime.c (50%) create mode 100644 src/runtime.h rename {launchd/src => support}/launchctl.c (68%) rename {launchd/src => support}/launchproxy.c (100%) rename {launchd/src => support}/wait4path.c (99%) create mode 100644 xcconfigs/common.xcconfig create mode 100644 xcconfigs/launchctl.xcconfig create mode 100644 xcconfigs/launchd.xcconfig create mode 100644 xcconfigs/liblaunch.xcconfig create mode 100644 xcscripts/SystemStarter-postflight.sh create mode 100644 xcscripts/launchctl-postflight.sh create mode 100644 xcscripts/launchd-postflight.sh create mode 100644 xcscripts/liblaunch-postflight.sh create mode 100644 xcsupport/osx_redirect_name diff --git a/launchd/src/IPC.c b/SystemStarter/IPC.c similarity index 100% rename from launchd/src/IPC.c rename to SystemStarter/IPC.c diff --git a/launchd/src/IPC.h b/SystemStarter/IPC.h similarity index 100% rename from launchd/src/IPC.h rename to SystemStarter/IPC.h diff --git a/launchd/src/StartupItemContext b/SystemStarter/StartupItemContext similarity index 100% rename from launchd/src/StartupItemContext rename to SystemStarter/StartupItemContext diff --git a/launchd/src/StartupItemContext.8 b/SystemStarter/StartupItemContext.8 similarity index 100% rename from launchd/src/StartupItemContext.8 rename to SystemStarter/StartupItemContext.8 diff --git a/launchd/src/StartupItems.c b/SystemStarter/StartupItems.c similarity index 100% rename from launchd/src/StartupItems.c rename to SystemStarter/StartupItems.c diff --git a/launchd/src/StartupItems.h b/SystemStarter/StartupItems.h similarity index 100% rename from launchd/src/StartupItems.h rename to SystemStarter/StartupItems.h diff --git a/launchd/src/SystemStarter.8 b/SystemStarter/SystemStarter.8 similarity index 100% rename from launchd/src/SystemStarter.8 rename to SystemStarter/SystemStarter.8 diff --git a/launchd/src/SystemStarter.c b/SystemStarter/SystemStarter.c similarity index 100% rename from launchd/src/SystemStarter.c rename to SystemStarter/SystemStarter.c diff --git a/launchd/src/SystemStarter.h b/SystemStarter/SystemStarter.h similarity index 100% rename from launchd/src/SystemStarter.h rename to SystemStarter/SystemStarter.h diff --git a/launchd/src/SystemStarterIPC.h b/SystemStarter/SystemStarterIPC.h similarity index 100% rename from launchd/src/SystemStarterIPC.h rename to SystemStarter/SystemStarterIPC.h diff --git a/launchd/src/com.apple.SystemStarter.plist b/SystemStarter/com.apple.SystemStarter.plist similarity index 100% rename from launchd/src/com.apple.SystemStarter.plist rename to SystemStarter/com.apple.SystemStarter.plist diff --git a/launchd/src/hostconfig b/SystemStarter/hostconfig similarity index 100% rename from launchd/src/hostconfig rename to SystemStarter/hostconfig diff --git a/launchd.xcodeproj/project.pbxproj b/launchd.xcodeproj/project.pbxproj index d8c88e1..c424676 100644 --- a/launchd.xcodeproj/project.pbxproj +++ b/launchd.xcodeproj/project.pbxproj @@ -50,20 +50,18 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 4B0A3103131F266E002DE2E5 /* events.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4B0A30FF131F24AC002DE2E5 /* events.defs */; settings = {ATTRIBUTES = (Server, ); }; }; - 4B0FB8EA1241FE3F00383109 /* domain.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4B0FB8E91241FE3F00383109 /* domain.defs */; settings = {ATTRIBUTES = (Server, ); }; }; - 4B10F1B90F43BE7E00875782 /* launchd_internal.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0BD0E8C8A2A00D41150 /* launchd_internal.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; - 4B10F1BA0F43BE7E00875782 /* protocol_vproc.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC3627DF0E9344BF0054F1A3 /* protocol_vproc.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; - 4B10F1BB0F43BE7E00875782 /* protocol_job_reply.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC3629160E9348390054F1A3 /* protocol_job_reply.defs */; }; + 4B10F1B90F43BE7E00875782 /* internal.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0BD0E8C8A2A00D41150 /* internal.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + 4B10F1BA0F43BE7E00875782 /* job.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC3627DF0E9344BF0054F1A3 /* job.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + 4B10F1BB0F43BE7E00875782 /* job_reply.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC3629160E9348390054F1A3 /* job_reply.defs */; }; 4B10F1BC0F43BE7E00875782 /* mach_exc.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC36291F0E9349410054F1A3 /* mach_exc.defs */; settings = {ATTRIBUTES = (Server, ); }; }; 4B10F1BD0F43BE7E00875782 /* notify.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC36290C0E93475F0054F1A3 /* notify.defs */; settings = {ATTRIBUTES = (Server, ); }; }; 4B10F1BE0F43BE7E00875782 /* launchd.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0C40E8C8A4700D41150 /* launchd.c */; }; - 4B10F1BF0F43BE7E00875782 /* launchd_runtime.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B50E8C8A1F00D41150 /* launchd_runtime.c */; settings = {COMPILER_FLAGS = "-I\"$SYMROOT\""; }; }; - 4B10F1C00F43BE7E00875782 /* launchd_runtime_kill.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B30E8C8A1F00D41150 /* launchd_runtime_kill.c */; }; - 4B10F1C10F43BE7E00875782 /* launchd_core_logic.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B70E8C8A1F00D41150 /* launchd_core_logic.c */; }; - 4B10F1C20F43BE7E00875782 /* launchd_unix_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B10E8C8A1F00D41150 /* launchd_unix_ipc.c */; }; - 4B10F1C30F43BE7E00875782 /* launchd_ktrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB15D0EA7D7B200B2AC84 /* launchd_ktrace.c */; }; - 4B10F1C40F43BE7E00875782 /* protocol_job_forward.defs in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB1BF0EA7E21C00B2AC84 /* protocol_job_forward.defs */; }; + 4B10F1BF0F43BE7E00875782 /* runtime.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B50E8C8A1F00D41150 /* runtime.c */; settings = {COMPILER_FLAGS = "-I\"$SYMROOT\""; }; }; + 4B10F1C00F43BE7E00875782 /* kill2.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B30E8C8A1F00D41150 /* kill2.c */; }; + 4B10F1C10F43BE7E00875782 /* core.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B70E8C8A1F00D41150 /* core.c */; }; + 4B10F1C20F43BE7E00875782 /* ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B10E8C8A1F00D41150 /* ipc.c */; }; + 4B10F1C30F43BE7E00875782 /* ktrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB15D0EA7D7B200B2AC84 /* ktrace.c */; }; + 4B10F1C40F43BE7E00875782 /* job_forward.defs in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB1BF0EA7E21C00B2AC84 /* job_forward.defs */; }; 4B10F1C60F43BE7E00875782 /* libbsm.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FC36292C0E934AA40054F1A3 /* libbsm.dylib */; }; 4B10F1C90F43BE7E00875782 /* launchd.conf.5 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC59A0C30E8C8A4700D41150 /* launchd.conf.5 */; }; 4B10F1CA0F43BE7E00875782 /* launchd.plist.5 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC59A0C10E8C8A4700D41150 /* launchd.plist.5 */; }; @@ -71,43 +69,48 @@ 4B10F1CD0F43BE7E00875782 /* rc.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC59A0F80E8C8AC300D41150 /* rc.8 */; }; 4B10F1CF0F43BE7E00875782 /* rc.netboot in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC59A0F90E8C8AC300D41150 /* rc.netboot */; }; 4B10F1E80F43BF5C00875782 /* launchctl.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0AE0E8C8A0E00D41150 /* launchctl.c */; settings = {COMPILER_FLAGS = "-I\"$SDKROOT\"/System/Library/Frameworks/System.framework/PrivateHeaders"; }; }; - 4B10F1EA0F43BF5C00875782 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC36283E0E93463C0054F1A3 /* IOKit.framework */; }; 4B10F1EB0F43BF5C00875782 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC3628070E9345E10054F1A3 /* CoreFoundation.framework */; }; 4B10F1EC0F43BF5C00875782 /* libedit.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FCD713730E95DE49001B0111 /* libedit.dylib */; settings = {ATTRIBUTES = (Weak, ); }; }; 4B10F1EF0F43BF5C00875782 /* launchctl.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC59A0AD0E8C8A0E00D41150 /* launchctl.1 */; }; + 4B1D128B143505CB00A2BDED /* log.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D1288143502DA00A2BDED /* log.c */; }; + 4B1D128C143505CD00A2BDED /* log.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D1288143502DA00A2BDED /* log.c */; }; 4B1D92010F8BDE7D00125940 /* launchd.ops in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4B1D91ED0F8BDE1A00125940 /* launchd.ops */; }; - 4B287733111A509400C07B35 /* launchd_helper.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4B287732111A509400C07B35 /* launchd_helper.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; - 4B28781B111A61A400C07B35 /* launchd_helper.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4B287732111A509400C07B35 /* launchd_helper.defs */; }; - 4B43EAF414101C5800E9E776 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B43EAF314101C5800E9E776 /* ServiceManagement.framework */; }; - 4B9A1C1F132759F700019C67 /* events.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4B0A30FF131F24AC002DE2E5 /* events.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + 4B287733111A509400C07B35 /* helper.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4B287732111A509400C07B35 /* helper.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + 4B28781B111A61A400C07B35 /* helper.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4B287732111A509400C07B35 /* helper.defs */; }; + 4B7B6DA8143BD343000BCC80 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC36283E0E93463C0054F1A3 /* IOKit.framework */; }; + 4B949DD5143D010F008712B9 /* libCrashReporterClient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B949DD4143D010F008712B9 /* libCrashReporterClient.a */; }; + 4B949DD6143D010F008712B9 /* libCrashReporterClient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B949DD4143D010F008712B9 /* libCrashReporterClient.a */; }; 4B9EDCA20EAFC77E00A78496 /* DiskArbitration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B9EDCA10EAFC77E00A78496 /* DiskArbitration.framework */; }; - 4BA2F5FD1243063D00C2AADD /* init.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4BA2F5FC1243063D00C2AADD /* init.defs */; }; - 4BF8727C1187A5F000CC7DB5 /* launchd_helper.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4B287732111A509400C07B35 /* launchd_helper.defs */; }; + 4BF8727C1187A5F000CC7DB5 /* helper.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4B287732111A509400C07B35 /* helper.defs */; }; + 60416D1B1402EE6900C190AA /* init.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4BA2F5FC1243063D00C2AADD /* init.defs */; }; + 60416D1D1402EE6A00C190AA /* init.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4BA2F5FC1243063D00C2AADD /* init.defs */; }; + 60416D1E1402EE7200C190AA /* domain.defs in Sources */ = {isa = PBXBuildFile; fileRef = 4B0FB8E91241FE3F00383109 /* domain.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + 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 /* launchd_ktrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB15D0EA7D7B200B2AC84 /* launchd_ktrace.c */; }; + 726056090EA7FCF200D65FE7 /* ktrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB15D0EA7D7B200B2AC84 /* ktrace.c */; }; 72AFE8090EFAF3D9004BDA46 /* libauditd.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 7215DE4B0EFAF2EC00ABD81E /* libauditd.dylib */; }; - 72FDB15F0EA7D7B200B2AC84 /* launchd_ktrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB15D0EA7D7B200B2AC84 /* launchd_ktrace.c */; }; - 72FDB1C00EA7E21C00B2AC84 /* protocol_job_forward.defs in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB1BF0EA7E21C00B2AC84 /* protocol_job_forward.defs */; }; + 72FDB15F0EA7D7B200B2AC84 /* ktrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB15D0EA7D7B200B2AC84 /* ktrace.c */; }; + 72FDB1C00EA7E21C00B2AC84 /* job_forward.defs in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB1BF0EA7E21C00B2AC84 /* job_forward.defs */; }; FC3627BA0E9343220054F1A3 /* StartupItems.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0FD0E8C8ADF00D41150 /* StartupItems.c */; }; FC3627BB0E93432A0054F1A3 /* SystemStarter.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A1000E8C8ADF00D41150 /* SystemStarter.c */; }; FC3627D40E93439B0054F1A3 /* StartupItemContext.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC59A0FE0E8C8ADF00D41150 /* StartupItemContext.8 */; }; FC3627D50E93439B0054F1A3 /* SystemStarter.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC59A1010E8C8ADF00D41150 /* SystemStarter.8 */; }; - FC3627E00E9344BF0054F1A3 /* protocol_vproc.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC3627DF0E9344BF0054F1A3 /* protocol_vproc.defs */; }; - FC3627E10E9344BF0054F1A3 /* protocol_vproc.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC3627DF0E9344BF0054F1A3 /* protocol_vproc.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + FC3627E00E9344BF0054F1A3 /* job.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC3627DF0E9344BF0054F1A3 /* job.defs */; }; + FC3627E10E9344BF0054F1A3 /* job.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC3627DF0E9344BF0054F1A3 /* job.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; FC3628080E9345E10054F1A3 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC3628070E9345E10054F1A3 /* CoreFoundation.framework */; }; FC3628090E9345E10054F1A3 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC3628070E9345E10054F1A3 /* CoreFoundation.framework */; }; FC36283F0E93463C0054F1A3 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC36283E0E93463C0054F1A3 /* IOKit.framework */; }; FC36290D0E93475F0054F1A3 /* notify.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC36290C0E93475F0054F1A3 /* notify.defs */; settings = {ATTRIBUTES = (Server, ); }; }; - FC3629170E9348390054F1A3 /* protocol_job_reply.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC3629160E9348390054F1A3 /* protocol_job_reply.defs */; }; + FC3629170E9348390054F1A3 /* job_reply.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC3629160E9348390054F1A3 /* job_reply.defs */; }; FC36292D0E934AA40054F1A3 /* libbsm.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FC36292C0E934AA40054F1A3 /* libbsm.dylib */; }; FC59A0A60E8C89C100D41150 /* IPC.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0A50E8C89C100D41150 /* IPC.c */; }; FC59A0AF0E8C8A0E00D41150 /* launchctl.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0AE0E8C8A0E00D41150 /* launchctl.c */; settings = {COMPILER_FLAGS = "-I\"$SDKROOT\"/System/Library/Frameworks/System.framework/PrivateHeaders"; }; }; - FC59A0B80E8C8A1F00D41150 /* launchd_unix_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B10E8C8A1F00D41150 /* launchd_unix_ipc.c */; }; - FC59A0B90E8C8A1F00D41150 /* launchd_runtime_kill.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B30E8C8A1F00D41150 /* launchd_runtime_kill.c */; }; - FC59A0BA0E8C8A1F00D41150 /* launchd_runtime.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B50E8C8A1F00D41150 /* launchd_runtime.c */; settings = {COMPILER_FLAGS = "-I\"$SYMROOT\""; }; }; - FC59A0BB0E8C8A1F00D41150 /* launchd_core_logic.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B70E8C8A1F00D41150 /* launchd_core_logic.c */; }; - FC59A0BF0E8C8A2A00D41150 /* launchd_internal.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0BD0E8C8A2A00D41150 /* launchd_internal.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + FC59A0B80E8C8A1F00D41150 /* ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B10E8C8A1F00D41150 /* ipc.c */; }; + FC59A0B90E8C8A1F00D41150 /* kill2.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B30E8C8A1F00D41150 /* kill2.c */; }; + FC59A0BA0E8C8A1F00D41150 /* runtime.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B50E8C8A1F00D41150 /* runtime.c */; settings = {COMPILER_FLAGS = "-I\"$SYMROOT\""; }; }; + FC59A0BB0E8C8A1F00D41150 /* core.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0B70E8C8A1F00D41150 /* core.c */; }; + FC59A0BF0E8C8A2A00D41150 /* internal.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0BD0E8C8A2A00D41150 /* internal.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; FC59A0C50E8C8A4700D41150 /* launchd.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0C40E8C8A4700D41150 /* launchd.c */; }; FC59A0DC0E8C8A6900D41150 /* launchproxy.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0DA0E8C8A6900D41150 /* launchproxy.c */; }; FC59A0ED0E8C8AA600D41150 /* vproc.h in Headers */ = {isa = PBXBuildFile; fileRef = FC59A0E20E8C8AA600D41150 /* vproc.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -372,82 +375,93 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 4B0A30FF131F24AC002DE2E5 /* events.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = events.defs; path = /usr/local/include/xpc/events.defs; sourceTree = SDKROOT; }; - 4B0A3100131F24AC002DE2E5 /* types.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = types.defs; path = /usr/local/include/xpc/types.defs; sourceTree = SDKROOT; }; - 4B0FB8E91241FE3F00383109 /* domain.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = domain.defs; path = /usr/local/include/xpc/domain.defs; sourceTree = SDKROOT; }; + 4B0A3100131F24AC002DE2E5 /* types.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = types.defs; path = usr/local/include/xpc/types.defs; sourceTree = SDKROOT; }; + 4B0FB8E91241FE3F00383109 /* domain.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = domain.defs; path = usr/local/include/xpc/domain.defs; sourceTree = SDKROOT; }; 4B10F1D30F43BE7E00875782 /* launchd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = launchd; sourceTree = BUILT_PRODUCTS_DIR; }; 4B10F1F30F43BF5C00875782 /* launchctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = launchctl; sourceTree = BUILT_PRODUCTS_DIR; }; - 4B1D91ED0F8BDE1A00125940 /* launchd.ops */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = launchd.ops; path = launchd/src/launchd.ops; sourceTree = ""; }; - 4B287732111A509400C07B35 /* launchd_helper.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = launchd_helper.defs; path = launchd/src/launchd_helper.defs; sourceTree = ""; }; + 4B1D12741433D79800A2BDED /* launchd.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = launchd.xcconfig; path = xcconfigs/launchd.xcconfig; sourceTree = ""; }; + 4B1D12751433D7BE00A2BDED /* liblaunch.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = liblaunch.xcconfig; path = xcconfigs/liblaunch.xcconfig; sourceTree = ""; }; + 4B1D12771433E5EB00A2BDED /* launchctl.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = launchctl.xcconfig; path = xcconfigs/launchctl.xcconfig; sourceTree = ""; }; + 4B1D12781433E70400A2BDED /* launchd-postflight.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = "launchd-postflight.sh"; path = "xcscripts/launchd-postflight.sh"; sourceTree = ""; }; + 4B1D12791433E76F00A2BDED /* liblaunch-postflight.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = "liblaunch-postflight.sh"; path = "xcscripts/liblaunch-postflight.sh"; sourceTree = ""; }; + 4B1D127A1433E7A400A2BDED /* launchctl-postflight.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = "launchctl-postflight.sh"; path = "xcscripts/launchctl-postflight.sh"; sourceTree = ""; }; + 4B1D127C1433E85A00A2BDED /* SystemStarter-postflight.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = "SystemStarter-postflight.sh"; path = "xcscripts/SystemStarter-postflight.sh"; sourceTree = ""; }; + 4B1D127D1433E91D00A2BDED /* common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = common.xcconfig; path = xcconfigs/common.xcconfig; sourceTree = ""; }; + 4B1D1288143502DA00A2BDED /* log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; name = log.c; path = src/log.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; + 4B1D128A143502F000A2BDED /* log.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = log.h; path = src/log.h; sourceTree = ""; }; + 4B1D91ED0F8BDE1A00125940 /* launchd.ops */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = launchd.ops; path = liblaunch/launchd.ops; sourceTree = ""; }; + 4B287732111A509400C07B35 /* helper.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = helper.defs; path = src/helper.defs; sourceTree = ""; }; 4B43EAF314101C5800E9E776 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = /System/Library/Frameworks/ServiceManagement.framework; sourceTree = ""; }; + 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; }; + 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 = launchd/src/config.h; sourceTree = ""; }; - 72FDB15D0EA7D7B200B2AC84 /* launchd_ktrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchd_ktrace.c; path = launchd/src/launchd_ktrace.c; sourceTree = ""; }; - 72FDB15E0EA7D7B200B2AC84 /* launchd_ktrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launchd_ktrace.h; path = launchd/src/launchd_ktrace.h; sourceTree = ""; }; - 72FDB1BF0EA7E21C00B2AC84 /* protocol_job_forward.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = protocol_job_forward.defs; path = launchd/src/protocol_job_forward.defs; sourceTree = ""; }; - FC3627DF0E9344BF0054F1A3 /* protocol_vproc.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = protocol_vproc.defs; path = launchd/src/protocol_vproc.defs; 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 = ""; }; + 72FDB15E0EA7D7B200B2AC84 /* ktrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ktrace.h; path = src/ktrace.h; sourceTree = ""; }; + 72FDB1BF0EA7E21C00B2AC84 /* job_forward.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = job_forward.defs; path = src/job_forward.defs; sourceTree = ""; }; + FC3627DF0E9344BF0054F1A3 /* job.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = job.defs; path = src/job.defs; sourceTree = ""; }; FC3628070E9345E10054F1A3 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; FC36283E0E93463C0054F1A3 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; - FC36290C0E93475F0054F1A3 /* notify.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = notify.defs; path = /usr/include/mach/notify.defs; sourceTree = SDKROOT; }; - FC3629160E9348390054F1A3 /* protocol_job_reply.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = protocol_job_reply.defs; path = launchd/src/protocol_job_reply.defs; sourceTree = ""; }; - FC36291F0E9349410054F1A3 /* mach_exc.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = mach_exc.defs; path = /usr/include/mach/mach_exc.defs; sourceTree = SDKROOT; }; + FC36290C0E93475F0054F1A3 /* notify.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = notify.defs; path = usr/include/mach/notify.defs; sourceTree = SDKROOT; }; + FC3629160E9348390054F1A3 /* job_reply.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = job_reply.defs; path = src/job_reply.defs; sourceTree = ""; }; + FC36291F0E9349410054F1A3 /* mach_exc.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = mach_exc.defs; path = usr/include/mach/mach_exc.defs; sourceTree = SDKROOT; }; FC36292C0E934AA40054F1A3 /* libbsm.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbsm.dylib; path = /usr/lib/libbsm.dylib; sourceTree = ""; }; FC59A0540E8C884700D41150 /* launchd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = launchd; sourceTree = BUILT_PRODUCTS_DIR; }; FC59A0600E8C885100D41150 /* liblaunch.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = liblaunch.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; FC59A06D0E8C888A00D41150 /* launchctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = launchctl; sourceTree = BUILT_PRODUCTS_DIR; }; FC59A0910E8C892300D41150 /* SystemStarter */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SystemStarter; sourceTree = BUILT_PRODUCTS_DIR; }; - FC59A0A40E8C89C100D41150 /* IPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IPC.h; path = launchd/src/IPC.h; sourceTree = SOURCE_ROOT; }; - FC59A0A50E8C89C100D41150 /* IPC.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = IPC.c; path = launchd/src/IPC.c; sourceTree = SOURCE_ROOT; }; - FC59A0AD0E8C8A0E00D41150 /* launchctl.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = launchctl.1; path = launchd/src/launchctl.1; sourceTree = SOURCE_ROOT; }; - FC59A0AE0E8C8A0E00D41150 /* launchctl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchctl.c; path = launchd/src/launchctl.c; sourceTree = SOURCE_ROOT; }; - FC59A0B00E8C8A1F00D41150 /* launchd_unix_ipc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launchd_unix_ipc.h; path = launchd/src/launchd_unix_ipc.h; sourceTree = ""; }; - FC59A0B10E8C8A1F00D41150 /* launchd_unix_ipc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchd_unix_ipc.c; path = launchd/src/launchd_unix_ipc.c; sourceTree = ""; }; - FC59A0B20E8C8A1F00D41150 /* launchd_runtime_kill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launchd_runtime_kill.h; path = launchd/src/launchd_runtime_kill.h; sourceTree = ""; }; - FC59A0B30E8C8A1F00D41150 /* launchd_runtime_kill.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchd_runtime_kill.c; path = launchd/src/launchd_runtime_kill.c; sourceTree = ""; }; - FC59A0B40E8C8A1F00D41150 /* launchd_runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launchd_runtime.h; path = launchd/src/launchd_runtime.h; sourceTree = ""; }; - FC59A0B50E8C8A1F00D41150 /* launchd_runtime.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchd_runtime.c; path = launchd/src/launchd_runtime.c; sourceTree = ""; }; - FC59A0B60E8C8A1F00D41150 /* launchd_core_logic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launchd_core_logic.h; path = launchd/src/launchd_core_logic.h; sourceTree = ""; }; - FC59A0B70E8C8A1F00D41150 /* launchd_core_logic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchd_core_logic.c; path = launchd/src/launchd_core_logic.c; sourceTree = ""; }; - FC59A0BC0E8C8A2A00D41150 /* launchd_mig_types.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = launchd_mig_types.defs; path = launchd/src/launchd_mig_types.defs; sourceTree = ""; }; - FC59A0BD0E8C8A2A00D41150 /* launchd_internal.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = launchd_internal.defs; path = launchd/src/launchd_internal.defs; sourceTree = ""; }; - FC59A0C00E8C8A3A00D41150 /* launchd.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = launchd.8; path = launchd/src/launchd.8; sourceTree = ""; }; - FC59A0C10E8C8A4700D41150 /* launchd.plist.5 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = launchd.plist.5; path = launchd/src/launchd.plist.5; sourceTree = ""; }; - FC59A0C20E8C8A4700D41150 /* launchd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launchd.h; path = launchd/src/launchd.h; sourceTree = ""; }; - FC59A0C30E8C8A4700D41150 /* launchd.conf.5 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = launchd.conf.5; path = launchd/src/launchd.conf.5; sourceTree = ""; }; - FC59A0C40E8C8A4700D41150 /* launchd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchd.c; path = launchd/src/launchd.c; sourceTree = ""; }; + FC59A0A40E8C89C100D41150 /* IPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IPC.h; path = SystemStarter/IPC.h; sourceTree = SOURCE_ROOT; }; + FC59A0A50E8C89C100D41150 /* IPC.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = IPC.c; path = SystemStarter/IPC.c; sourceTree = SOURCE_ROOT; }; + FC59A0AD0E8C8A0E00D41150 /* launchctl.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = launchctl.1; path = man/launchctl.1; sourceTree = SOURCE_ROOT; }; + FC59A0AE0E8C8A0E00D41150 /* launchctl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchctl.c; path = support/launchctl.c; sourceTree = SOURCE_ROOT; }; + FC59A0B00E8C8A1F00D41150 /* ipc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ipc.h; path = src/ipc.h; sourceTree = ""; }; + FC59A0B10E8C8A1F00D41150 /* ipc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ipc.c; path = src/ipc.c; sourceTree = ""; }; + FC59A0B20E8C8A1F00D41150 /* kill2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = kill2.h; path = src/kill2.h; sourceTree = ""; }; + FC59A0B30E8C8A1F00D41150 /* kill2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = kill2.c; path = src/kill2.c; sourceTree = ""; }; + FC59A0B40E8C8A1F00D41150 /* runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = runtime.h; path = src/runtime.h; sourceTree = ""; }; + FC59A0B50E8C8A1F00D41150 /* runtime.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = runtime.c; path = src/runtime.c; sourceTree = ""; }; + FC59A0B60E8C8A1F00D41150 /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = core.h; path = src/core.h; sourceTree = ""; }; + FC59A0B70E8C8A1F00D41150 /* core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = core.c; path = src/core.c; sourceTree = ""; }; + FC59A0BC0E8C8A2A00D41150 /* job_types.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = job_types.defs; path = src/job_types.defs; sourceTree = ""; }; + FC59A0BD0E8C8A2A00D41150 /* internal.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = internal.defs; path = src/internal.defs; sourceTree = ""; }; + FC59A0C00E8C8A3A00D41150 /* launchd.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = launchd.8; path = man/launchd.8; sourceTree = ""; }; + FC59A0C10E8C8A4700D41150 /* launchd.plist.5 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = launchd.plist.5; path = man/launchd.plist.5; sourceTree = ""; }; + FC59A0C20E8C8A4700D41150 /* launchd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launchd.h; path = src/launchd.h; sourceTree = ""; }; + FC59A0C30E8C8A4700D41150 /* launchd.conf.5 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = launchd.conf.5; path = man/launchd.conf.5; sourceTree = ""; }; + FC59A0C40E8C8A4700D41150 /* launchd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchd.c; path = src/launchd.c; sourceTree = ""; }; FC59A0CE0E8C8A5C00D41150 /* launchproxy */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = launchproxy; sourceTree = BUILT_PRODUCTS_DIR; }; - FC59A0DA0E8C8A6900D41150 /* launchproxy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchproxy.c; path = launchd/src/launchproxy.c; sourceTree = SOURCE_ROOT; }; - FC59A0DB0E8C8A6900D41150 /* launchproxy.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = launchproxy.8; path = launchd/src/launchproxy.8; sourceTree = SOURCE_ROOT; }; - FC59A0E20E8C8AA600D41150 /* vproc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vproc.h; path = launchd/src/vproc.h; sourceTree = ""; }; - FC59A0E30E8C8AA600D41150 /* vproc_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vproc_priv.h; path = launchd/src/vproc_priv.h; sourceTree = ""; }; - FC59A0E40E8C8AA600D41150 /* vproc_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vproc_internal.h; path = launchd/src/vproc_internal.h; sourceTree = ""; }; - FC59A0E50E8C8AA600D41150 /* libvproc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = libvproc.c; path = launchd/src/libvproc.c; sourceTree = SOURCE_ROOT; }; - FC59A0E60E8C8AA600D41150 /* launch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launch.h; path = launchd/src/launch.h; sourceTree = ""; }; - FC59A0E70E8C8AA600D41150 /* launch_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launch_priv.h; path = launchd/src/launch_priv.h; sourceTree = ""; }; - FC59A0E80E8C8AA600D41150 /* launch_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launch_internal.h; path = launchd/src/launch_internal.h; sourceTree = ""; }; - FC59A0E90E8C8AA600D41150 /* liblaunch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = liblaunch.c; path = launchd/src/liblaunch.c; sourceTree = SOURCE_ROOT; }; - FC59A0EA0E8C8AA600D41150 /* bootstrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bootstrap.h; path = launchd/src/bootstrap.h; sourceTree = ""; }; - FC59A0EB0E8C8AA600D41150 /* bootstrap_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bootstrap_priv.h; path = launchd/src/bootstrap_priv.h; sourceTree = ""; }; - FC59A0EC0E8C8AA600D41150 /* libbootstrap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = libbootstrap.c; path = launchd/src/libbootstrap.c; sourceTree = SOURCE_ROOT; }; - FC59A0F80E8C8AC300D41150 /* rc.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = rc.8; path = launchd/src/rc.8; sourceTree = ""; }; - FC59A0F90E8C8AC300D41150 /* rc.netboot */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = rc.netboot; path = launchd/src/rc.netboot; sourceTree = ""; }; - FC59A0FA0E8C8AC300D41150 /* rc.common */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = rc.common; path = launchd/src/rc.common; sourceTree = ""; }; - FC59A0FB0E8C8ACE00D41150 /* reboot2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reboot2.h; path = launchd/src/reboot2.h; sourceTree = SOURCE_ROOT; }; - FC59A0FD0E8C8ADF00D41150 /* StartupItems.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = StartupItems.c; path = launchd/src/StartupItems.c; sourceTree = SOURCE_ROOT; }; - FC59A0FE0E8C8ADF00D41150 /* StartupItemContext.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = StartupItemContext.8; path = launchd/src/StartupItemContext.8; sourceTree = SOURCE_ROOT; }; - FC59A0FF0E8C8ADF00D41150 /* StartupItemContext */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = StartupItemContext; path = launchd/src/StartupItemContext; sourceTree = SOURCE_ROOT; }; - FC59A1000E8C8ADF00D41150 /* SystemStarter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SystemStarter.c; path = launchd/src/SystemStarter.c; sourceTree = SOURCE_ROOT; }; - FC59A1010E8C8ADF00D41150 /* SystemStarter.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = SystemStarter.8; path = launchd/src/SystemStarter.8; sourceTree = SOURCE_ROOT; }; - FC59A1020E8C8ADF00D41150 /* StartupItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StartupItems.h; path = launchd/src/StartupItems.h; sourceTree = SOURCE_ROOT; }; - FC59A1030E8C8ADF00D41150 /* SystemStarterIPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SystemStarterIPC.h; path = launchd/src/SystemStarterIPC.h; sourceTree = SOURCE_ROOT; }; - FC59A1040E8C8ADF00D41150 /* SystemStarter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SystemStarter.h; path = launchd/src/SystemStarter.h; sourceTree = SOURCE_ROOT; }; - FCD713200E95D5D3001B0111 /* com.apple.SystemStarter.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.SystemStarter.plist; path = launchd/src/com.apple.SystemStarter.plist; sourceTree = ""; }; + FC59A0DA0E8C8A6900D41150 /* launchproxy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchproxy.c; path = support/launchproxy.c; sourceTree = SOURCE_ROOT; }; + FC59A0DB0E8C8A6900D41150 /* launchproxy.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = launchproxy.8; path = man/launchproxy.8; sourceTree = SOURCE_ROOT; }; + FC59A0E20E8C8AA600D41150 /* vproc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vproc.h; path = liblaunch/vproc.h; sourceTree = ""; }; + FC59A0E30E8C8AA600D41150 /* vproc_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vproc_priv.h; path = liblaunch/vproc_priv.h; sourceTree = ""; }; + FC59A0E40E8C8AA600D41150 /* vproc_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vproc_internal.h; path = liblaunch/vproc_internal.h; sourceTree = ""; }; + FC59A0E50E8C8AA600D41150 /* libvproc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = libvproc.c; path = liblaunch/libvproc.c; sourceTree = SOURCE_ROOT; }; + FC59A0E60E8C8AA600D41150 /* launch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launch.h; path = liblaunch/launch.h; sourceTree = ""; }; + FC59A0E70E8C8AA600D41150 /* launch_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launch_priv.h; path = liblaunch/launch_priv.h; sourceTree = ""; }; + FC59A0E80E8C8AA600D41150 /* launch_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launch_internal.h; path = liblaunch/launch_internal.h; sourceTree = ""; }; + FC59A0E90E8C8AA600D41150 /* liblaunch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = liblaunch.c; path = liblaunch/liblaunch.c; sourceTree = SOURCE_ROOT; }; + FC59A0EA0E8C8AA600D41150 /* bootstrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bootstrap.h; path = liblaunch/bootstrap.h; sourceTree = ""; }; + FC59A0EB0E8C8AA600D41150 /* bootstrap_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bootstrap_priv.h; path = liblaunch/bootstrap_priv.h; sourceTree = ""; }; + FC59A0EC0E8C8AA600D41150 /* libbootstrap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = libbootstrap.c; path = liblaunch/libbootstrap.c; sourceTree = SOURCE_ROOT; }; + FC59A0F80E8C8AC300D41150 /* rc.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = rc.8; path = man/rc.8; sourceTree = ""; }; + FC59A0F90E8C8AC300D41150 /* rc.netboot */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = rc.netboot; path = rc/rc.netboot; sourceTree = ""; }; + FC59A0FA0E8C8AC300D41150 /* rc.common */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = rc.common; path = rc/rc.common; sourceTree = ""; }; + FC59A0FB0E8C8ACE00D41150 /* reboot2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reboot2.h; path = liblaunch/reboot2.h; sourceTree = SOURCE_ROOT; }; + FC59A0FD0E8C8ADF00D41150 /* StartupItems.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = StartupItems.c; path = SystemStarter/StartupItems.c; sourceTree = SOURCE_ROOT; }; + FC59A0FE0E8C8ADF00D41150 /* StartupItemContext.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = StartupItemContext.8; path = SystemStarter/StartupItemContext.8; sourceTree = SOURCE_ROOT; }; + FC59A0FF0E8C8ADF00D41150 /* StartupItemContext */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = StartupItemContext; path = SystemStarter/StartupItemContext; sourceTree = SOURCE_ROOT; }; + FC59A1000E8C8ADF00D41150 /* SystemStarter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SystemStarter.c; path = SystemStarter/SystemStarter.c; sourceTree = SOURCE_ROOT; }; + FC59A1010E8C8ADF00D41150 /* SystemStarter.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = SystemStarter.8; path = SystemStarter/SystemStarter.8; sourceTree = SOURCE_ROOT; }; + FC59A1020E8C8ADF00D41150 /* StartupItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StartupItems.h; path = SystemStarter/StartupItems.h; sourceTree = SOURCE_ROOT; }; + FC59A1030E8C8ADF00D41150 /* SystemStarterIPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SystemStarterIPC.h; path = SystemStarter/SystemStarterIPC.h; sourceTree = SOURCE_ROOT; }; + FC59A1040E8C8ADF00D41150 /* SystemStarter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SystemStarter.h; path = SystemStarter/SystemStarter.h; sourceTree = SOURCE_ROOT; }; + FCD713200E95D5D3001B0111 /* com.apple.SystemStarter.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.SystemStarter.plist; path = SystemStarter/com.apple.SystemStarter.plist; sourceTree = ""; }; FCD7132B0E95D64D001B0111 /* wait4path */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = wait4path; sourceTree = BUILT_PRODUCTS_DIR; }; - FCD713370E95D69E001B0111 /* wait4path.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = wait4path.1; path = launchd/src/wait4path.1; sourceTree = ""; }; - FCD713380E95D69E001B0111 /* wait4path.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wait4path.c; path = launchd/src/wait4path.c; sourceTree = ""; }; - FCD713520E95D7B3001B0111 /* hostconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = hostconfig; path = launchd/src/hostconfig; sourceTree = ""; }; + FCD713370E95D69E001B0111 /* wait4path.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = wait4path.1; path = man/wait4path.1; sourceTree = ""; }; + FCD713380E95D69E001B0111 /* wait4path.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wait4path.c; path = support/wait4path.c; sourceTree = ""; }; + FCD713520E95D7B3001B0111 /* hostconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = hostconfig; path = SystemStarter/hostconfig; sourceTree = ""; }; FCD713730E95DE49001B0111 /* libedit.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libedit.dylib; path = /usr/lib/libedit.dylib; sourceTree = ""; }; /* End PBXFileReference section */ @@ -464,8 +478,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4B10F1EA0F43BF5C00875782 /* IOKit.framework in Frameworks */, 4B10F1EB0F43BF5C00875782 /* CoreFoundation.framework in Frameworks */, + 4B7B6DA8143BD343000BCC80 /* IOKit.framework in Frameworks */, 4B10F1EC0F43BF5C00875782 /* libedit.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -476,13 +490,7 @@ files = ( FC36292D0E934AA40054F1A3 /* libbsm.dylib in Frameworks */, 7215DE4C0EFAF2EC00ABD81E /* libauditd.dylib in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - FC59A05E0E8C885100D41150 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( + 4B949DD5143D010F008712B9 /* libCrashReporterClient.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -490,11 +498,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4B43EAF414101C5800E9E776 /* ServiceManagement.framework in Frameworks */, FCC841CC0EA7138700C01666 /* IOKit.framework in Frameworks */, FC3628080E9345E10054F1A3 /* CoreFoundation.framework in Frameworks */, FCD713740E95DE49001B0111 /* libedit.dylib in Frameworks */, 72AFE8090EFAF3D9004BDA46 /* libauditd.dylib in Frameworks */, + 4B949DD6143D010F008712B9 /* libCrashReporterClient.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -525,68 +533,114 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 4B0A3102131F24B3002DE2E5 /* XPC */ = { + 4B1D12731433D77400A2BDED /* xcconfigs */ = { isa = PBXGroup; children = ( - 4B0A3100131F24AC002DE2E5 /* types.defs */, - 4B0FB8E91241FE3F00383109 /* domain.defs */, - 4B0A30FF131F24AC002DE2E5 /* events.defs */, - 4BA2F5FC1243063D00C2AADD /* init.defs */, + 4B1D127D1433E91D00A2BDED /* common.xcconfig */, + 4B1D12741433D79800A2BDED /* launchd.xcconfig */, + 4B1D12751433D7BE00A2BDED /* liblaunch.xcconfig */, + 4B1D12771433E5EB00A2BDED /* launchctl.xcconfig */, ); - name = XPC; + name = xcconfigs; sourceTree = ""; }; - 4B8D8239132C5F400081FD4E /* Mach */ = { + 4B1D12761433D94200A2BDED /* xcscripts */ = { isa = PBXGroup; children = ( - FC36291F0E9349410054F1A3 /* mach_exc.defs */, - FC36290C0E93475F0054F1A3 /* notify.defs */, + 4B1D12781433E70400A2BDED /* launchd-postflight.sh */, + 4B1D12791433E76F00A2BDED /* liblaunch-postflight.sh */, + 4B1D127A1433E7A400A2BDED /* launchctl-postflight.sh */, + 4B1D127C1433E85A00A2BDED /* SystemStarter-postflight.sh */, + ); + name = xcscripts; + sourceTree = ""; + }; + 4B2086731433CBEF006A5B71 /* man */ = { + isa = PBXGroup; + children = ( + FC59A0C00E8C8A3A00D41150 /* launchd.8 */, + FC59A0C10E8C8A4700D41150 /* launchd.plist.5 */, + FC59A0C30E8C8A4700D41150 /* launchd.conf.5 */, + FC59A0AD0E8C8A0E00D41150 /* launchctl.1 */, + FC59A0DB0E8C8A6900D41150 /* launchproxy.8 */, + FCD713370E95D69E001B0111 /* wait4path.1 */, + FC59A0F80E8C8AC300D41150 /* rc.8 */, ); - name = Mach; + name = man; sourceTree = ""; }; - 4B9EDCD60EAFD11000A78496 /* MIG */ = { + 4B2086771433CE05006A5B71 /* public */ = { isa = PBXGroup; children = ( - FC59A0BC0E8C8A2A00D41150 /* launchd_mig_types.defs */, - 72FDB1BF0EA7E21C00B2AC84 /* protocol_job_forward.defs */, - FC3629160E9348390054F1A3 /* protocol_job_reply.defs */, - FC3627DF0E9344BF0054F1A3 /* protocol_vproc.defs */, - FC59A0BD0E8C8A2A00D41150 /* launchd_internal.defs */, - 4B287732111A509400C07B35 /* launchd_helper.defs */, - ); - name = MIG; + FC59A0E60E8C8AA600D41150 /* launch.h */, + FC59A0EA0E8C8AA600D41150 /* bootstrap.h */, + FC59A0E20E8C8AA600D41150 /* vproc.h */, + ); + name = public; + sourceTree = ""; + }; + 4B2086781433CE0E006A5B71 /* private */ = { + isa = PBXGroup; + children = ( + FC59A0E70E8C8AA600D41150 /* launch_priv.h */, + FC59A0EB0E8C8AA600D41150 /* bootstrap_priv.h */, + FC59A0E30E8C8AA600D41150 /* vproc_priv.h */, + FC59A0FB0E8C8ACE00D41150 /* reboot2.h */, + ); + name = private; sourceTree = ""; }; - 4B9EDCD70EAFD14500A78496 /* Source */ = { + 4B2086791433CE49006A5B71 /* internal */ = { isa = PBXGroup; children = ( + 4B1D91ED0F8BDE1A00125940 /* launchd.ops */, + FC59A0E80E8C8AA600D41150 /* launch_internal.h */, + FC59A0E40E8C8AA600D41150 /* vproc_internal.h */, 721FBEA50EA7ABC40057462B /* config.h */, FC59A0C20E8C8A4700D41150 /* launchd.h */, - FC59A0C40E8C8A4700D41150 /* launchd.c */, - FC59A0B00E8C8A1F00D41150 /* launchd_unix_ipc.h */, - FC59A0B10E8C8A1F00D41150 /* launchd_unix_ipc.c */, - FC59A0B20E8C8A1F00D41150 /* launchd_runtime_kill.h */, - FC59A0B30E8C8A1F00D41150 /* launchd_runtime_kill.c */, - FC59A0B40E8C8A1F00D41150 /* launchd_runtime.h */, - FC59A0B50E8C8A1F00D41150 /* launchd_runtime.c */, - FC59A0B60E8C8A1F00D41150 /* launchd_core_logic.h */, - FC59A0B70E8C8A1F00D41150 /* launchd_core_logic.c */, - 72FDB15E0EA7D7B200B2AC84 /* launchd_ktrace.h */, - 72FDB15D0EA7D7B200B2AC84 /* launchd_ktrace.c */, - ); - name = Source; + FC59A0B00E8C8A1F00D41150 /* ipc.h */, + FC59A0B20E8C8A1F00D41150 /* kill2.h */, + FC59A0B40E8C8A1F00D41150 /* runtime.h */, + 72FDB15E0EA7D7B200B2AC84 /* ktrace.h */, + FC59A0B60E8C8A1F00D41150 /* core.h */, + 4B1D128A143502F000A2BDED /* log.h */, + ); + name = internal; sourceTree = ""; }; - 4B9EDCD80EAFD15D00A78496 /* Documentation */ = { + 4B20867A1433CE7E006A5B71 /* support */ = { isa = PBXGroup; children = ( - FC59A0F80E8C8AC300D41150 /* rc.8 */, - FC59A0C10E8C8A4700D41150 /* launchd.plist.5 */, - FC59A0C30E8C8A4700D41150 /* launchd.conf.5 */, - FC59A0C00E8C8A3A00D41150 /* launchd.8 */, + FC59A0AE0E8C8A0E00D41150 /* launchctl.c */, + FC59A0DA0E8C8A6900D41150 /* launchproxy.c */, + FCD713380E95D69E001B0111 /* wait4path.c */, ); - name = Documentation; + name = support; + sourceTree = ""; + }; + 4B9EDCD70EAFD14500A78496 /* src */ = { + isa = PBXGroup; + children = ( + FC59A0BC0E8C8A2A00D41150 /* job_types.defs */, + FC3627DF0E9344BF0054F1A3 /* job.defs */, + FC3629160E9348390054F1A3 /* job_reply.defs */, + 72FDB1BF0EA7E21C00B2AC84 /* job_forward.defs */, + FC59A0BD0E8C8A2A00D41150 /* internal.defs */, + 4B287732111A509400C07B35 /* helper.defs */, + 4B0A3100131F24AC002DE2E5 /* types.defs */, + 4B0FB8E91241FE3F00383109 /* domain.defs */, + 4BA2F5FC1243063D00C2AADD /* init.defs */, + FC36291F0E9349410054F1A3 /* mach_exc.defs */, + FC36290C0E93475F0054F1A3 /* notify.defs */, + FC59A0C40E8C8A4700D41150 /* launchd.c */, + FC59A0B10E8C8A1F00D41150 /* ipc.c */, + FC59A0B30E8C8A1F00D41150 /* kill2.c */, + FC59A0B50E8C8A1F00D41150 /* runtime.c */, + 72FDB15D0EA7D7B200B2AC84 /* ktrace.c */, + FC59A0B70E8C8A1F00D41150 /* core.c */, + 4B1D1288143502DA00A2BDED /* log.c */, + ); + name = src; sourceTree = ""; }; 4B9EDCD90EAFD19800A78496 /* rc */ = { @@ -598,9 +652,18 @@ name = rc; sourceTree = ""; }; + 4BC868A6143CFC8C00B46F40 /* xcsupport */ = { + isa = PBXGroup; + children = ( + 4BC868A8143CFD0300B46F40 /* osx_redirect_name */, + ); + name = xcsupport; + sourceTree = ""; + }; FC36280C0E9345F60054F1A3 /* Frameworks */ = { isa = PBXGroup; children = ( + 4B949DD4143D010F008712B9 /* libCrashReporterClient.a */, 4B43EAF314101C5800E9E776 /* ServiceManagement.framework */, 4B9EDCA10EAFC77E00A78496 /* DiskArbitration.framework */, FC36292C0E934AA40054F1A3 /* libbsm.dylib */, @@ -615,30 +678,23 @@ FC59A03D0E8C87FD00D41150 = { isa = PBXGroup; children = ( - FC59A04E0E8C883300D41150 /* launchd */, + 4B1D12731433D77400A2BDED /* xcconfigs */, + 4B1D12761433D94200A2BDED /* xcscripts */, + 4BC868A6143CFC8C00B46F40 /* xcsupport */, + 4B2086771433CE05006A5B71 /* public */, + 4B2086781433CE0E006A5B71 /* private */, + 4B2086791433CE49006A5B71 /* internal */, + 4B9EDCD70EAFD14500A78496 /* src */, FC59A0E10E8C8A9400D41150 /* liblaunch */, - FC59A0A90E8C89C900D41150 /* launchctl */, - FC59A0C80E8C8A4E00D41150 /* launchproxy */, + 4B20867A1433CE7E006A5B71 /* support */, + 4B2086731433CBEF006A5B71 /* man */, + 4B9EDCD90EAFD19800A78496 /* rc */, FC59A0A00E8C899600D41150 /* SystemStarter */, - FCD713330E95D65F001B0111 /* wait4path */, FC36280C0E9345F60054F1A3 /* Frameworks */, FC59A0550E8C884700D41150 /* Products */, ); sourceTree = ""; }; - FC59A04E0E8C883300D41150 /* launchd */ = { - isa = PBXGroup; - children = ( - 4B8D8239132C5F400081FD4E /* Mach */, - 4B0A3102131F24B3002DE2E5 /* XPC */, - 4B9EDCD60EAFD11000A78496 /* MIG */, - 4B9EDCD70EAFD14500A78496 /* Source */, - 4B9EDCD90EAFD19800A78496 /* rc */, - 4B9EDCD80EAFD15D00A78496 /* Documentation */, - ); - name = launchd; - sourceTree = ""; - }; FC59A0550E8C884700D41150 /* Products */ = { isa = PBXGroup; children = ( @@ -657,69 +713,32 @@ FC59A0A00E8C899600D41150 /* SystemStarter */ = { isa = PBXGroup; children = ( - FC59A0FD0E8C8ADF00D41150 /* StartupItems.c */, - FC59A0FE0E8C8ADF00D41150 /* StartupItemContext.8 */, - FC59A0FF0E8C8ADF00D41150 /* StartupItemContext */, - FC59A1000E8C8ADF00D41150 /* SystemStarter.c */, - FC59A1010E8C8ADF00D41150 /* SystemStarter.8 */, + FCD713200E95D5D3001B0111 /* com.apple.SystemStarter.plist */, FC59A1020E8C8ADF00D41150 /* StartupItems.h */, FC59A1030E8C8ADF00D41150 /* SystemStarterIPC.h */, FC59A1040E8C8ADF00D41150 /* SystemStarter.h */, FC59A0A40E8C89C100D41150 /* IPC.h */, + FC59A0FD0E8C8ADF00D41150 /* StartupItems.c */, + FC59A1000E8C8ADF00D41150 /* SystemStarter.c */, FC59A0A50E8C89C100D41150 /* IPC.c */, - FCD713200E95D5D3001B0111 /* com.apple.SystemStarter.plist */, + FC59A0FF0E8C8ADF00D41150 /* StartupItemContext */, FCD713520E95D7B3001B0111 /* hostconfig */, + FC59A0FE0E8C8ADF00D41150 /* StartupItemContext.8 */, + FC59A1010E8C8ADF00D41150 /* SystemStarter.8 */, ); name = SystemStarter; sourceTree = ""; }; - FC59A0A90E8C89C900D41150 /* launchctl */ = { - isa = PBXGroup; - children = ( - FC59A0AD0E8C8A0E00D41150 /* launchctl.1 */, - FC59A0AE0E8C8A0E00D41150 /* launchctl.c */, - ); - name = launchctl; - sourceTree = ""; - }; - FC59A0C80E8C8A4E00D41150 /* launchproxy */ = { - isa = PBXGroup; - children = ( - FC59A0DB0E8C8A6900D41150 /* launchproxy.8 */, - FC59A0DA0E8C8A6900D41150 /* launchproxy.c */, - ); - name = launchproxy; - sourceTree = ""; - }; FC59A0E10E8C8A9400D41150 /* liblaunch */ = { isa = PBXGroup; children = ( - 4B1D91ED0F8BDE1A00125940 /* launchd.ops */, - FC59A0FB0E8C8ACE00D41150 /* reboot2.h */, - FC59A0E20E8C8AA600D41150 /* vproc.h */, - FC59A0E30E8C8AA600D41150 /* vproc_priv.h */, - FC59A0E40E8C8AA600D41150 /* vproc_internal.h */, FC59A0E50E8C8AA600D41150 /* libvproc.c */, - FC59A0E60E8C8AA600D41150 /* launch.h */, - FC59A0E70E8C8AA600D41150 /* launch_priv.h */, - FC59A0E80E8C8AA600D41150 /* launch_internal.h */, FC59A0E90E8C8AA600D41150 /* liblaunch.c */, - FC59A0EA0E8C8AA600D41150 /* bootstrap.h */, - FC59A0EB0E8C8AA600D41150 /* bootstrap_priv.h */, FC59A0EC0E8C8AA600D41150 /* libbootstrap.c */, ); name = liblaunch; sourceTree = ""; }; - FCD713330E95D65F001B0111 /* wait4path */ = { - isa = PBXGroup; - children = ( - FCD713370E95D69E001B0111 /* wait4path.1 */, - FCD713380E95D69E001B0111 /* wait4path.c */, - ); - name = wait4path; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -807,7 +826,6 @@ 4B1D91F40F8BDE6800125940 /* CopyFiles */, FC59A05C0E8C885100D41150 /* Headers */, FC59A05D0E8C885100D41150 /* Sources */, - FC59A05E0E8C885100D41150 /* Frameworks */, FC7B88230EA7280100542082 /* ShellScript */, ); buildRules = ( @@ -865,7 +883,6 @@ FC59A0CB0E8C8A5C00D41150 /* Sources */, FC59A0CC0E8C8A5C00D41150 /* Frameworks */, FCD713120E95D554001B0111 /* CopyFiles */, - FC7B87F20EA71A6200542082 /* ShellScript */, ); buildRules = ( ); @@ -883,7 +900,6 @@ FCD713280E95D64D001B0111 /* Sources */, FCD713290E95D64D001B0111 /* Frameworks */, FCD7134D0E95D728001B0111 /* CopyFiles */, - FC7B87EE0EA71A4900542082 /* ShellScript */, ); buildRules = ( ); @@ -900,7 +916,7 @@ FC59A03F0E8C87FD00D41150 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0420; + LastUpgradeCheck = 0450; }; buildConfigurationList = FC59A0420E8C87FD00D41150 /* Build configuration list for PBXProject "launchd" */; compatibilityVersion = "Xcode 3.2"; @@ -921,13 +937,13 @@ 726056200EA8088C00D65FE7 /* embedded */, FC59A07A0E8C88BB00D41150 /* launchd_libs */, FC59A0530E8C884700D41150 /* launchd */, + FC59A05F0E8C885100D41150 /* liblaunch */, FC59A06C0E8C888A00D41150 /* launchctl */, 4B10F1B70F43BE7E00875782 /* launchd-embedded */, 4B10F1E60F43BF5C00875782 /* launchctl-embedded */, FC59A0CD0E8C8A5C00D41150 /* launchproxy */, FCD7132A0E95D64D001B0111 /* wait4path */, FC59A0900E8C892300D41150 /* SystemStarter */, - FC59A05F0E8C885100D41150 /* liblaunch */, ); }; /* End PBXProject section */ @@ -944,7 +960,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "/Developer/Makefiles/bin/compress-man-pages.pl -d \"$DSTROOT\" /usr/share/man\n/bin/mkdir -p \"$DSTROOT/private/var/db/launchd.db/com.apple.launchd\"\n/usr/sbin/chown root:wheel \"$DSTROOT/private/var/db/launchd.db\"\n/usr/sbin/chown root:wheel \"$DSTROOT/private/var/db/launchd.db/com.apple.launchd\"\n\n"; + shellScript = "set -ex\n\n/bin/bash ${BUILD_XCSCRIPTS_DIR}/launchd-postflight.sh"; showEnvVarsInLog = 0; }; 4B10F1F00F43BF5C00875782 /* ShellScript */ = { @@ -958,7 +974,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "install -o \"$INSTALL_OWNER\" -g \"$INSTALL_GROUP\" -m 0755 -d \"$DSTROOT\"/System/Library/LaunchAgents\ninstall -o \"$INSTALL_OWNER\" -g \"$INSTALL_GROUP\" -m 0755 -d \"$DSTROOT\"/System/Library/LaunchDaemons\ninstall -o \"$INSTALL_OWNER\" -g \"$INSTALL_GROUP\" -m 0755 -d \"$DSTROOT\"/Library/LaunchAgents\ninstall -o \"$INSTALL_OWNER\" -g \"$INSTALL_GROUP\" -m 0755 -d \"$DSTROOT\"/Library/LaunchDaemons\n/Developer/Makefiles/bin/compress-man-pages.pl -d \"$DSTROOT\" /usr/share/man"; + shellScript = "set -ex\n\n/bin/bash ${BUILD_XCSCRIPTS_DIR}/launchctl-postflight.sh"; showEnvVarsInLog = 0; }; FC7B87B20EA7195F00542082 /* ShellScript */ = { @@ -972,35 +988,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "/Developer/Makefiles/bin/compress-man-pages.pl -d \"$DSTROOT\" /usr/share/man\n/bin/mkdir -p \"$DSTROOT/private/var/db/launchd.db/com.apple.launchd\"\n/usr/sbin/chown root:wheel \"$DSTROOT/private/var/db/launchd.db\"\n/usr/sbin/chown root:wheel \"$DSTROOT/private/var/db/launchd.db/com.apple.launchd\"\n/bin/mkdir -p \"$DSTROOT/private/etc/mach_init.d\"\n/bin/mkdir -p \"$DSTROOT/private/etc/mach_init_per_user.d\"\n/bin/mkdir -p \"$DSTROOT/private/etc/mach_init_per_login_session.d\"\n/usr/sbin/chown root:wheel \"$DSTROOT/private/etc/mach_init.d\"\n/usr/sbin/chown root:wheel \"$DSTROOT/private/etc/mach_init_per_user.d\"\n/usr/sbin/chown root:wheel \"$DSTROOT/private/etc/mach_init_per_login_session.d\""; - showEnvVarsInLog = 0; - }; - FC7B87EE0EA71A4900542082 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "/Developer/Makefiles/bin/compress-man-pages.pl -d \"$DSTROOT\" /usr/share/man"; - showEnvVarsInLog = 0; - }; - FC7B87F20EA71A6200542082 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "/Developer/Makefiles/bin/compress-man-pages.pl -d \"$DSTROOT\" /usr/share/man"; + shellScript = "set -ex\n\n/bin/bash ${BUILD_XCSCRIPTS_DIR}/launchd-postflight.sh"; showEnvVarsInLog = 0; }; FC7B88230EA7280100542082 /* ShellScript */ = { @@ -1014,7 +1002,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "install -o \"$INSTALL_OWNER\" -g \"$INSTALL_GROUP\" -m 0755 -d \"$DSTROOT\"/usr/include/servers\nmv \"$DSTROOT\"/usr/include/bootstrap.h \"$DSTROOT\"/usr/include/servers/\nln -sf bootstrap.h \"$DSTROOT\"/usr/include/servers/bootstrap_defs.h"; + shellScript = "set -ex\n\n/bin/bash ${BUILD_XCSCRIPTS_DIR}/liblaunch-postflight.sh"; showEnvVarsInLog = 0; }; FCC8427E0EA7175100C01666 /* ShellScript */ = { @@ -1028,7 +1016,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "install -o \"$INSTALL_OWNER\" -g \"$INSTALL_GROUP\" -m 0755 -d \"$DSTROOT\"/System/Library/StartupItems\ninstall -o \"$INSTALL_OWNER\" -g \"$INSTALL_GROUP\" -m 0755 -d \"$DSTROOT\"/Library/StartupItems\n/Developer/Makefiles/bin/compress-man-pages.pl -d \"$DSTROOT\" /usr/share/man"; + shellScript = "set -ex\n\n/bin/bash ${BUILD_XCSCRIPTS_DIR}/SystemStarter-postflight.sh"; showEnvVarsInLog = 0; }; FCC842860EA718C400C01666 /* ShellScript */ = { @@ -1042,7 +1030,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "install -o \"$INSTALL_OWNER\" -g \"$INSTALL_GROUP\" -m 0755 -d \"$DSTROOT\"/System/Library/LaunchAgents\ninstall -o \"$INSTALL_OWNER\" -g \"$INSTALL_GROUP\" -m 0755 -d \"$DSTROOT\"/System/Library/LaunchDaemons\ninstall -o \"$INSTALL_OWNER\" -g \"$INSTALL_GROUP\" -m 0755 -d \"$DSTROOT\"/Library/LaunchAgents\ninstall -o \"$INSTALL_OWNER\" -g \"$INSTALL_GROUP\" -m 0755 -d \"$DSTROOT\"/Library/LaunchDaemons\n/Developer/Makefiles/bin/compress-man-pages.pl -d \"$DSTROOT\" /usr/share/man"; + shellScript = "set -ex\n\n/bin/bash ${BUILD_XCSCRIPTS_DIR}/launchctl-postflight.sh"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -1052,20 +1040,22 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4B9A1C1F132759F700019C67 /* events.defs in Sources */, - 4BF8727C1187A5F000CC7DB5 /* launchd_helper.defs in Sources */, - 4B10F1B90F43BE7E00875782 /* launchd_internal.defs in Sources */, - 4B10F1BA0F43BE7E00875782 /* protocol_vproc.defs in Sources */, - 4B10F1BB0F43BE7E00875782 /* protocol_job_reply.defs in Sources */, + 60416D1D1402EE6A00C190AA /* init.defs in Sources */, + 60416D1F1402EE7300C190AA /* domain.defs in Sources */, + 4BF8727C1187A5F000CC7DB5 /* helper.defs in Sources */, + 4B10F1B90F43BE7E00875782 /* internal.defs in Sources */, + 4B10F1BA0F43BE7E00875782 /* job.defs in Sources */, + 4B10F1BB0F43BE7E00875782 /* job_reply.defs in Sources */, 4B10F1BC0F43BE7E00875782 /* mach_exc.defs in Sources */, 4B10F1BD0F43BE7E00875782 /* notify.defs in Sources */, + 4B10F1C40F43BE7E00875782 /* job_forward.defs in Sources */, 4B10F1BE0F43BE7E00875782 /* launchd.c in Sources */, - 4B10F1BF0F43BE7E00875782 /* launchd_runtime.c in Sources */, - 4B10F1C00F43BE7E00875782 /* launchd_runtime_kill.c in Sources */, - 4B10F1C10F43BE7E00875782 /* launchd_core_logic.c in Sources */, - 4B10F1C20F43BE7E00875782 /* launchd_unix_ipc.c in Sources */, - 4B10F1C30F43BE7E00875782 /* launchd_ktrace.c in Sources */, - 4B10F1C40F43BE7E00875782 /* protocol_job_forward.defs in Sources */, + 4B10F1BF0F43BE7E00875782 /* runtime.c in Sources */, + 4B10F1C00F43BE7E00875782 /* kill2.c in Sources */, + 4B10F1C10F43BE7E00875782 /* core.c in Sources */, + 4B10F1C20F43BE7E00875782 /* ipc.c in Sources */, + 4B10F1C30F43BE7E00875782 /* ktrace.c in Sources */, + 4B1D128C143505CD00A2BDED /* log.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1081,22 +1071,22 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4B28781B111A61A400C07B35 /* launchd_helper.defs in Sources */, - FC59A0BF0E8C8A2A00D41150 /* launchd_internal.defs in Sources */, - FC3627E10E9344BF0054F1A3 /* protocol_vproc.defs in Sources */, - FC3629170E9348390054F1A3 /* protocol_job_reply.defs in Sources */, + 4B28781B111A61A400C07B35 /* helper.defs in Sources */, + FC59A0BF0E8C8A2A00D41150 /* internal.defs in Sources */, + FC3627E10E9344BF0054F1A3 /* job.defs in Sources */, + FC3629170E9348390054F1A3 /* job_reply.defs in Sources */, 726055EC0EA7EC2400D65FE7 /* mach_exc.defs in Sources */, FC36290D0E93475F0054F1A3 /* notify.defs in Sources */, - 4B0FB8EA1241FE3F00383109 /* domain.defs in Sources */, - 4B0A3103131F266E002DE2E5 /* events.defs in Sources */, - 4BA2F5FD1243063D00C2AADD /* init.defs in Sources */, - 72FDB1C00EA7E21C00B2AC84 /* protocol_job_forward.defs in Sources */, + 60416D1E1402EE7200C190AA /* domain.defs in Sources */, + 60416D1B1402EE6900C190AA /* init.defs in Sources */, + 72FDB1C00EA7E21C00B2AC84 /* job_forward.defs in Sources */, FC59A0C50E8C8A4700D41150 /* launchd.c in Sources */, - FC59A0BA0E8C8A1F00D41150 /* launchd_runtime.c in Sources */, - FC59A0B90E8C8A1F00D41150 /* launchd_runtime_kill.c in Sources */, - FC59A0BB0E8C8A1F00D41150 /* launchd_core_logic.c in Sources */, - FC59A0B80E8C8A1F00D41150 /* launchd_unix_ipc.c in Sources */, - 72FDB15F0EA7D7B200B2AC84 /* launchd_ktrace.c in Sources */, + FC59A0BA0E8C8A1F00D41150 /* runtime.c in Sources */, + FC59A0B90E8C8A1F00D41150 /* kill2.c in Sources */, + FC59A0BB0E8C8A1F00D41150 /* core.c in Sources */, + FC59A0B80E8C8A1F00D41150 /* ipc.c in Sources */, + 72FDB15F0EA7D7B200B2AC84 /* ktrace.c in Sources */, + 4B1D128B143505CB00A2BDED /* log.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1104,12 +1094,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4B287733111A509400C07B35 /* helper.defs in Sources */, + FC3627E00E9344BF0054F1A3 /* job.defs in Sources */, FC59A0F40E8C8AA600D41150 /* liblaunch.c in Sources */, FC59A0F00E8C8AA600D41150 /* libvproc.c in Sources */, FC59A0F70E8C8AA600D41150 /* libbootstrap.c in Sources */, - FC3627E00E9344BF0054F1A3 /* protocol_vproc.defs in Sources */, - 726056090EA7FCF200D65FE7 /* launchd_ktrace.c in Sources */, - 4B287733111A509400C07B35 /* launchd_helper.defs in Sources */, + 726056090EA7FCF200D65FE7 /* ktrace.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1205,39 +1195,33 @@ /* Begin XCBuildConfiguration section */ 4B10F1D20F43BE7E00875782 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4B1D12741433D79800A2BDED /* launchd.xcconfig */; buildSettings = { - GCC_PREPROCESSOR_DEFINITIONS = ( - __LAUNCH_DISABLE_XPC_SUPPORT__, - XPC_BUILDING_LAUNCHD, - ); - HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/local/include"; - INSTALL_PATH = /sbin; - OTHER_MIGFLAGS = "-DXPC_BUILDING_LAUNCHD -I$(PROJECT_DIR)/launchd/src/ -I$(SDKROOT)/usr/local/include"; - PRODUCT_NAME = launchd; + SUPPORTED_PLATFORMS = iphoneos; }; name = Release; }; 4B10F1F20F43BF5C00875782 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4B1D12771433E5EB00A2BDED /* launchctl.xcconfig */; buildSettings = { INSTALL_PATH = /bin; PRODUCT_NAME = launchctl; + SUPPORTED_PLATFORMS = iphoneos; }; name = Release; }; 726056210EA8088D00D65FE7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - PRODUCT_NAME = embedded; - ZERO_LINK = NO; + PRODUCT_NAME = "launchd-embedded"; }; name = Release; }; FC59A0410E8C87FD00D41150 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = YES; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; @@ -1268,91 +1252,42 @@ }; FC59A0580E8C884800D41150 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4B1D12741433D79800A2BDED /* launchd.xcconfig */; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_DYNAMIC_NO_PIC = NO; - GCC_PREPROCESSOR_DEFINITIONS = ""; - HEADER_SEARCH_PATHS = ""; - INSTALL_PATH = /sbin; - OTHER_CFLAGS = ( - "-D__MigTypeCheck=1", - "-Dmig_external=__private_extern__", - "-D_DARWIN_USE_64_BIT_INODE=1", - ); - OTHER_MIGFLAGS = "-DXPC_BUILDING_LAUNCHD -I$(PROJECT_DIR)/launchd/src/"; - PRODUCT_NAME = launchd; }; name = Release; }; FC59A0620E8C885100D41150 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4B1D12751433D7BE00A2BDED /* liblaunch.xcconfig */; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - BUILD_VARIANTS = ( - normal, - debug, - profile, - ); - CRASHREPORTER_LINKER_FLAGS = ""; - "CRASHREPORTER_LINKER_FLAGS[sdk=macosx*][arch=*]" = "-lCrashReporterClient"; - "CRASHREPORTER_LINKER_FLAGS[sdk=macosx10.6][arch=*]" = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; - EXECUTABLE_PREFIX = lib; - INSTALLHDRS_SCRIPT_PHASE = YES; - INSTALL_PATH = /usr/lib/system; - LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)"; - OTHER_CFLAGS = ( - "-D__MigTypeCheck=1", - "-Dmig_external=__private_extern__", - "-D__DARWIN_NON_CANCELABLE=1", - "-D_DARWIN_USE_64_BIT_INODE=1", - ); - OTHER_LDFLAGS = ( - "-Wl,-umbrella,System", - "$(CRASHREPORTER_LINKER_FLAGS)", - ); - PRODUCT_NAME = launch; - PUBLIC_HEADERS_FOLDER_PATH = /usr/include; - STRIP_INSTALLED_PRODUCT = YES; - STRIP_STYLE = "non-global"; - VERSION_INFO_PREFIX = _; }; name = Release; }; FC59A0700E8C888A00D41150 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4B1D12771433E5EB00A2BDED /* launchctl.xcconfig */; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_DYNAMIC_NO_PIC = NO; - INSTALL_PATH = /bin; - PRODUCT_NAME = launchctl; }; name = Release; }; FC59A0770E8C88AC00D41150 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - PRODUCT_NAME = default; - ZERO_LINK = NO; + PRODUCT_NAME = "launchd-default"; }; name = Release; }; FC59A07C0E8C88BC00D41150 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - PRODUCT_NAME = launchd_libs; - ZERO_LINK = NO; + PRODUCT_NAME = liblaunch; }; name = Release; }; FC59A0940E8C892400D41150 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4B1D127D1433E91D00A2BDED /* common.xcconfig */; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; GCC_DYNAMIC_NO_PIC = NO; @@ -1364,21 +1299,25 @@ }; FC59A0D10E8C8A5C00D41150 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4B1D127D1433E91D00A2BDED /* common.xcconfig */; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; GCC_DYNAMIC_NO_PIC = NO; INSTALL_PATH = /usr/libexec; PRODUCT_NAME = launchproxy; + SUPPORTED_PLATFORMS = "macosx iphoneos"; }; name = Release; }; FCD7132E0E95D64E001B0111 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4B1D127D1433E91D00A2BDED /* common.xcconfig */; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; GCC_DYNAMIC_NO_PIC = NO; INSTALL_PATH = /bin; PRODUCT_NAME = wait4path; + SUPPORTED_PLATFORMS = "macosx iphoneos"; }; name = Release; }; diff --git a/launchd/AUTHORS b/launchd/AUTHORS deleted file mode 100644 index 9881eeb..0000000 --- a/launchd/AUTHORS +++ /dev/null @@ -1,4 +0,0 @@ -Dave Zarzycki zarzycki@apple.com Most everything here. -Fred Sanchez wsanchez@apple.com The original SystemStarter. -BSD et. al. init -CMU et. al. mach_init diff --git a/launchd/COPYING b/launchd/COPYING deleted file mode 100644 index d645695..0000000 --- a/launchd/COPYING +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/launchd/doc/HOWTO.html b/launchd/doc/HOWTO.html deleted file mode 100644 index a166903..0000000 --- a/launchd/doc/HOWTO.html +++ /dev/null @@ -1,270 +0,0 @@ - - -

Getting Started With Launchd

- -

Launchd is rather simple actually. Far simpler than you might think.

- -

Before We Get Started

- -

Launchd, in an effort to be consistent with other Apple software, uses the -Property List file format for storing configuration information. Apple's -Property List APIs provide for three different file formats for storing a -property list to disk. A plain text option, a binary option, and an XML text -option. For the remainder of this HOWTO, we will use the XML varient to show -what a configuration file looks like.

- -

The basics:

- -

For the simplest of scenarios, launchd just keeps a process alive. A simple "hello -world" example of that would be:

- -
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>Label</key>
-        <string>com.example.sleep</string>
-        <key>ProgramArguments</key>
-        <array>
-                <string>sleep</string>
-                <string>100</string>
-        </array>
-        <key>OnDemand</key>
-        <false/>
-</dict>
-</plist>
-
- -

In the above example, we have three keys to our top level dictionary. The first -is the Label which is what is used to uniquely identify jobs when interacting -with launchd. The second is ProgramArguments which for its value, we have an -array of strings which represent the tokenized arguments and the program to -run. The third and final key is OnDemand which overrides the default value of -true with false thereby instructing launchd to always try and keep this job -running. That's it! A Label, some ProgramArguments and OnDemand set to false is all -you need to keep a daemon alive with launchd!

- -

Now if you've ever written a daemon before, you've either called the -daemon() function or written one yourself. With launchd, that is -not only unnecessary, but unsupported. If you try and run a daemon you didn't -write under launchd, you must, at the very least, find a configuration option to -keep the daemon from daemonizing itself so that launchd can monitor it.

- -

Going beyond the basics with optional keys:

- -

There are many optional keys available and documented in the launchd.plist -man page, so we'll only give a few optional, but common examples:

- -
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>Label</key>
-        <string>com.example.sleep</string>
-        <key>ProgramArguments</key>
-        <array>
-                <string>sleep</string>
-                <string>100</string>
-        </array>
-        <key>OnDemand</key>
-        <false/>
-        <key>UserName</key>
-        <string>daemon</string>
-        <key>GroupName</key>
-        <string>daemon</string>
-        <key>EnvironmentVariables</key>
-	<dict>
-		<key>FOO</key>
-		<string>bar</string>
-	</dict>
-</dict>
-</plist>
-
- -

In the above example, we see that the job is run as a certain user and group, and additionally has an extra environment variable set.

- -

Debugging tricks:

- -

The following example will enable core dumps, set standard out and error to -go to a log file and to instruct launchd to temporarily bump up the debug level -of launchd's loggging while acting on behave of your job (remember to adjust -your syslog.conf accordingly):

- -
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>Label</key>
-        <string>com.example.sleep</string>
-        <key>ProgramArguments</key>
-        <array>
-                <string>sleep</string>
-                <string>100</string>
-        </array>
-        <key>OnDemand</key>
-        <false/>
-        <key>StandardOutPath</key>
-        <string>/var/log/myjob.log</string>
-        <key>StandardErrorPath</key>
-        <string>/var/log/myjob.log</string>
-        <key>Debug</key>
-        <true/>
-        <key>SoftResourceLimits</key>
-	<dict>
-	        <key>Core</key>
-		<integer>9223372036854775807</integer>
-	</dict>
-        <key>HardResourceLimits</key>
-	<dict>
-	        <key>Core</key>
-		<integer>9223372036854775807</integer>
-	</dict>
-</dict>
-</plist>
-
- -

But what if I don't want or expect my job to run continuously?

- -

The basics of on demand launching:

- -

Launchd provides a multitude of different criteria that can be used to specify -when a job should be started. It is important to note that launchd will only -run one instance of your job though. Therefore, if your on demand job -malfunctions, you are guaranteed that launchd will not spawn additional copies -when your criteria is satisfied once more in the future.

- -

Starting a job periodically:

- -

Here is an example on how to have a job start every five minutes (300 seconds):

- -
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>Label</key>
-        <string>com.example.touchsomefile</string>
-        <key>ProgramArguments</key>
-        <array>
-                <string>touch</string>
-		<string>/tmp/helloworld</string>
-        </array>
-        <key>StartInterval</key>
-	<integer>300</integer>
-</dict>
-</plist>
-
- -

Sometimes you want a job started on a calendar based interval. The following example will start the job on the 11th minute of the 11th hour every day (using a 24 hour clock system) on the 11th day of the month. Like the Unix cron subsystem, any missing key of the StartCalendarInterval dictionary is treated as a wildcard:

- -
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>Label</key>
-        <string>com.example.touchsomefile</string>
-        <key>ProgramArguments</key>
-        <array>
-                <string>touch</string>
-		<string>/tmp/helloworld</string>
-        </array>
-        <key>StartCalendarInterval</key>
-        <dict>
-        	<key>Minute</key>
-		<integer>11</integer>
-		<key>Hour</key>
-		<integer>11</integer>
-		<key>Day</key>
-		<integer>11</integer>
-        </dict>
-</dict>
-
- -

Starting a job based on file system activity:

- -

The following example will start the job whenever any of the paths being watched change for whatever reason:

- -
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>Label</key>
-	<string>com.example.watchetchostconfig</string>
-        <key>ProgramArguments</key>
-        <array>
-                <string>syslog</string>
-                <string>-s</string>
-                <string>-l</string>
-                <string>notice</string>
-		<string>somebody touched /etc/hostconfig</string>
-        </array>
-	<key>WatchPaths</key>
-        <array>
-        	<string>/etc/hostconfig</string>
-        </array>
-</dict>
-
- -

An additional file system trigger is the notion of a queue directory. Launchd will star your job whenever -the given directories are non-empty and will keep your job running as long as those directories are not empty:

- -
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>Label</key>
-	<string>com.example.mailpush</string>
-        <key>ProgramArguments</key>
-        <array>
-		<string>my_custom_mail_push_tool</string>
-        </array>
-	<key>QueueDirectories</key>
-        <array>
-        	<string>/var/spool/mymailqdir</string>
-        </array>
-</dict>
-
- -

Inetd Emulation

- -

Launchd will happily emulate inetd style daemon semantics:

- -
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>Label</key>
-	<string>com.example.telnetd</string>
-        <key>ProgramArguments</key>
-        <array>
-		<string>/usr/libexec/telnetd</string>
-        </array>
-	<key>inetdCompatibility</key>
-        <dict>
-        	<key>Wait</key>
-        	<false/>
-        </dict>
-        <key>Sockets</key>
-        <dict>
-        	<key>Listeners</key>
-        	<dict>
-        		<key>SockServiceName</key>
-        		<string>telnet</string>
-        		<key>SockType</key>
-        		<string>stream</string>
-        	</dict>
-        </dict>
-</dict>
-
- -

TBD

- - - diff --git a/launchd/doc/StartupItem-NOTES.rtf b/launchd/doc/StartupItem-NOTES.rtf deleted file mode 100644 index 4000ad7..0000000 --- a/launchd/doc/StartupItem-NOTES.rtf +++ /dev/null @@ -1,96 +0,0 @@ -{\rtf1\mac\ansicpg10000\cocoartf100 -{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;\f1\fswiss\fcharset77 Helvetica;\f2\fmodern\fcharset77 Courier; -\f3\fswiss\fcharset77 Helvetica-Oblique;} -{\colortbl;\red255\green255\blue255;} -\vieww12840\viewh12820\viewkind0 -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f0\b\fs24 \cf0 Logistics of Startup Items:\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f1\b0 \cf0 \ - Startup items are directory bundles which contain, at a minimum, an executable file and a property list text file. For a startup item named "Foo", the bundle will be a directory named "Foo" containing (at a minimum) an executable "Foo" and a plist file "StartupParameters.plist".\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f0\b \cf0 Search Paths for Startup Items:\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f1\b0 \cf0 \ - Startup items may be placed in the "Library" subdirectory of the primary file domains ("System", "Local", and "Network"). The search order is defined by routines defined in NSSystemDirectories.h: Local, then Network, then System. However, because the Network mounts have not been established at the beginning of system startup, bundles in /Network is currently not searched; this may be fixed later such that /Network is searched when ti becomes available. This search order does not define the startup order, but it does effect the handling of conflicts between bundles which provide the same services.\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f0\b \cf0 Startup Actions:\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f1\b0 \cf0 \ - Presently, SystemStarter looks for an executable file with the name of the bundle (eg. Foo/Foo), and runs that file with a single argument.\ -\ - \'a5 A "start" argument indicates that the service(s) provided by the item are expected to start if they are configured to run. The program may opt to do nothing if the service is not configured to run.\ -\ - \'a5 A "stop" argument indicates that the service(s) provided by the item are expected to stop if it is running, regardless of whether it is configured to run.\ -\ - \'a5 A "restart" argument indicates that the service(s) provided by the item are expected to one of two things:\ - \'a5 Stop if the service is running, then start if the service is configured to run. (Same as stop followed by start.)\ - \'a5 If the service is running and not configured to run, stop the service; if the service is not running and configured to run, start the service; if the service is running and it is configured to run, reconfigure the service (eg. send the server a HUP signal).\ -\ - Startup items should take no action if they receive an unknown request and should therefore take care not to ignore the argument altogether; for example, a "stop" argument should most certainly not cause the item to start a service.\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f0\b \cf0 Item Launch Ordering:\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f1\b0 \cf0 \ - The plist file contains parameters which tell SystemStarter some information about the executable, such as what services is provides, which services are prerequisites to its use, and so on. The plist contains the following attributes:\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f2 \cf0 \{\ - Description = "blah blah";\ - Provides = ("service", ...);\ - Requires = ("service", ...);\ - Uses = ("service", ...);\ - OrderPreference = "time";\ - Messages =\ - \{\ - start = "Starting blah.";\ - stop = "Stopping blah.";\ - \}\ - \}\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f1 \cf0 \ - Note that while the above example is writing in the old NeXT-style property list format for compactness, the new XML property lists are also handled. You may prefer using PropertyListEditor.app to editing the property list files manually.\ -\ - Provides is an array that declares the services are provided by this bundle. A typical bundle provides a single service. Two bundles may not provide the same service; should multiple bundles which provide the same service be installed on a system, the first one encountered will be run, while the others will be disabled. It is therefore undesireable to provide multiple services in a single bundle unless they are co-dependent, as the overriding of one will effectively override all services in a given bundle (see also "Search Paths for Startup Items").\ -\ - Requires and Uses comprise the primary method for sorting bundles into the startup order. Requires is an array of services, provided by other bundles, that must be successfully started before the bundle can be run. If no such service is provided by any other bundle, the requiring bundle will not run. Uses is similar to Requires in that the bundle will attempt wait for the listed services before running, but it will still launch even if no such service can be provided by another bundle.\ -\ - OrderPreference provides a hint as to the ordering used when a set of bundles are all ready to load. Bundles which have their prerequisites met (that is, all Requires services are launched and all Uses services are either launched or deemed unavailable) are prioritized in this order, based on OrderPreference:\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f2 \cf0 First\ - Early\ - None (default)\ - Late\ - Last\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f1 \cf0 \ - Note that -\f3\i other than the above ordering rules, there are no guarantees about the startup order of items -\f1\i0 . That is, if multiple items are prioritized equally given the above constraints, there is no rule for which starts first. You must use the dependency mechanism to ensure the correct dependencies have been met. Note also that OrderPreference is merely a suggestion, and that SystemStarter may opt to disregard it. In particular, startup items are run parallel, and items which have dependencies met will be run without waiting for items of a lower OrderPreference to complete.\ -\ - Description is a general-use string describing the item, for use by Admin tools. The Messages property provides strings which are displayed by SystemStarter during startup and shutdown.\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f0\b \cf0 Shutdown:\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural - -\f1\b0 \cf0 \ - The intent is to add a shutdown sequence in the future so that the computer can be brought down more cleanly and give services a change to store state before exiting. The mechanism for this is still in the design stage.\ -} \ No newline at end of file diff --git a/launchd/doc/sampled.c b/launchd/doc/sampled.c deleted file mode 100644 index 6cb8779..0000000 --- a/launchd/doc/sampled.c +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "launch.h" - -static void -ack_mach_port(launch_data_t o, const char *name, void *context __attribute__((unused))) -{ - mach_port_t p = launch_data_get_machport(o); - - mach_port_deallocate(mach_task_self(), p); - - syslog(LOG_NOTICE, "Ignoring Mach service: %s", name); -} - -int -main(void) -{ - struct timespec timeout = { 60, 0 }; - struct sockaddr_storage ss; - socklen_t slen = sizeof(ss); - struct kevent kev; - launch_data_t tmp, resp, msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); - size_t i; - int kq; - - openlog(getprogname(), LOG_PERROR|LOG_PID|LOG_CONS, LOG_DAEMON); - - if (-1 == (kq = kqueue())) { - syslog(LOG_ERR, "kqueue(): %m"); - exit(EXIT_FAILURE); - } - - if ((resp = launch_msg(msg)) == NULL) { - syslog(LOG_ERR, "launch_msg(\"" LAUNCH_KEY_CHECKIN "\") IPC failure: %m"); - exit(EXIT_FAILURE); - } - - if (LAUNCH_DATA_ERRNO == launch_data_get_type(resp)) { - errno = launch_data_get_errno(resp); - if (errno == EACCES) - syslog(LOG_ERR, "Check-in failed. Did you forget to set ServiceIPC == true in your plist?"); - else - syslog(LOG_ERR, "Check-in failed: %m"); - exit(EXIT_FAILURE); - } - - tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_TIMEOUT); - if (tmp) - timeout.tv_sec = launch_data_get_integer(tmp); - - tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_MACHSERVICES); - if (tmp) { - launch_data_dict_iterate(tmp, ack_mach_port, NULL); - } - - tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS); - if (NULL == tmp) { - syslog(LOG_ERR, "No sockets found to answer requests on!"); - exit(EXIT_FAILURE); - } - - if (launch_data_dict_get_count(tmp) > 1) { - syslog(LOG_WARNING, "Some sockets will be ignored!"); - } - - tmp = launch_data_dict_lookup(tmp, "SampleListeners"); - if (NULL == tmp) { - syslog(LOG_ERR, "No known sockets found to answer requests on!"); - exit(EXIT_FAILURE); - } - - for (i = 0; i < launch_data_array_get_count(tmp); i++) { - launch_data_t tmpi = launch_data_array_get_index(tmp, i); - - EV_SET(&kev, launch_data_get_fd(tmpi), EVFILT_READ, EV_ADD, 0, 0, NULL); - if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) { - syslog(LOG_DEBUG, "kevent(): %m"); - exit(EXIT_FAILURE); - } - } - - launch_data_free(msg); - launch_data_free(resp); - - for (;;) { - FILE *c; - int r; - - if ((r = kevent(kq, NULL, 0, &kev, 1, &timeout)) == -1) { - syslog(LOG_ERR, "kevent(): %m"); - exit(EXIT_FAILURE); - } else if (r == 0) { - exit(EXIT_SUCCESS); - } - - if ((r = accept(kev.ident, (struct sockaddr *)&ss, &slen)) == -1) { - syslog(LOG_ERR, "accept(): %m"); - continue; /* this isn't fatal */ - } - - c = fdopen(r, "r+"); - - if (c) { - fprintf(c, "hello world!\n"); - fclose(c); - } else { - close(r); - } - } -} diff --git a/launchd/doc/sampled.plist b/launchd/doc/sampled.plist deleted file mode 100644 index 421f480..0000000 --- a/launchd/doc/sampled.plist +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - Label - com.example.sampled - - ProgramArguments - - - - sampled - - MachServices - - - - com.apple.sampled.something - - - Sockets - - - - SampleListeners - - - - - SockServiceName - 12345 - - - - - ServiceIPC - - - diff --git a/launchd/src/config.h b/launchd/src/config.h deleted file mode 100644 index 4c12664..0000000 --- a/launchd/src/config.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef __CONFIG_H__ -#define __CONFIG_H__ -#include -#define HAVE_QUARANTINE TARGET_HAVE_QUARANTINE -#define HAVE_SANDBOX TARGET_HAVE_SANDBOX -#define HAVE_LIBAUDITD !TARGET_OS_EMBEDDED -#endif /* __CONFIG_H__ */ diff --git a/launchd/src/launch.h b/launchd/src/launch.h deleted file mode 100644 index 61bf323..0000000 --- a/launchd/src/launch.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_APACHE_LICENSE_HEADER_START@ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @APPLE_APACHE_LICENSE_HEADER_END@ - */ -#ifndef _LAUNCH_H_ -#define _LAUNCH_H_ - -#include -#include -#include -#include - -#pragma GCC visibility push(default) - -__BEGIN_DECLS - -#ifdef __GNUC__ -#define __ld_normal __attribute__((__nothrow__)) -#define __ld_setter __attribute__((__nothrow__, __nonnull__)) -#define __ld_getter __attribute__((__nothrow__, __nonnull__, __pure__, __warn_unused_result__)) -#define __ld_iterator(x, y) __attribute__((__nonnull__(x, y))) -#define __ld_allocator __attribute__((__nothrow__, __malloc__, __nonnull__, __warn_unused_result__)) -#else -#define __ld_normal -#define __ld_setter -#define __ld_getter -#define __ld_iterator(x, y) -#define __ld_allocator -#endif - - -#define LAUNCH_KEY_SUBMITJOB "SubmitJob" -#define LAUNCH_KEY_REMOVEJOB "RemoveJob" -#define LAUNCH_KEY_STARTJOB "StartJob" -#define LAUNCH_KEY_STOPJOB "StopJob" -#define LAUNCH_KEY_GETJOB "GetJob" -#define LAUNCH_KEY_GETJOBS "GetJobs" -#define LAUNCH_KEY_CHECKIN "CheckIn" - -#define LAUNCH_JOBKEY_LABEL "Label" -#define LAUNCH_JOBKEY_DISABLED "Disabled" -#define LAUNCH_JOBKEY_USERNAME "UserName" -#define LAUNCH_JOBKEY_GROUPNAME "GroupName" -#define LAUNCH_JOBKEY_TIMEOUT "TimeOut" -#define LAUNCH_JOBKEY_EXITTIMEOUT "ExitTimeOut" -#define LAUNCH_JOBKEY_INITGROUPS "InitGroups" -#define LAUNCH_JOBKEY_SOCKETS "Sockets" -#define LAUNCH_JOBKEY_MACHSERVICES "MachServices" -#define LAUNCH_JOBKEY_MACHSERVICELOOKUPPOLICIES "MachServiceLookupPolicies" -#define LAUNCH_JOBKEY_INETDCOMPATIBILITY "inetdCompatibility" -#define LAUNCH_JOBKEY_ENABLEGLOBBING "EnableGlobbing" -#define LAUNCH_JOBKEY_PROGRAMARGUMENTS "ProgramArguments" -#define LAUNCH_JOBKEY_PROGRAM "Program" -#define LAUNCH_JOBKEY_ONDEMAND "OnDemand" -#define LAUNCH_JOBKEY_KEEPALIVE "KeepAlive" -#define LAUNCH_JOBKEY_LIMITLOADTOHOSTS "LimitLoadToHosts" -#define LAUNCH_JOBKEY_LIMITLOADFROMHOSTS "LimitLoadFromHosts" -#define LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE "LimitLoadToSessionType" -#define LAUNCH_JOBKEY_LIMITLOADTOHARDWARE "LimitLoadToHardware" -#define LAUNCH_JOBKEY_LIMITLOADFROMHARDWARE "LimitLoadFromHardware" -#define LAUNCH_JOBKEY_RUNATLOAD "RunAtLoad" -#define LAUNCH_JOBKEY_ROOTDIRECTORY "RootDirectory" -#define LAUNCH_JOBKEY_WORKINGDIRECTORY "WorkingDirectory" -#define LAUNCH_JOBKEY_ENVIRONMENTVARIABLES "EnvironmentVariables" -#define LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES "UserEnvironmentVariables" -#define LAUNCH_JOBKEY_UMASK "Umask" -#define LAUNCH_JOBKEY_NICE "Nice" -#define LAUNCH_JOBKEY_HOPEFULLYEXITSFIRST "HopefullyExitsFirst" -#define LAUNCH_JOBKEY_HOPEFULLYEXITSLAST "HopefullyExitsLast" -#define LAUNCH_JOBKEY_LOWPRIORITYIO "LowPriorityIO" -#define LAUNCH_JOBKEY_SESSIONCREATE "SessionCreate" -#define LAUNCH_JOBKEY_STARTONMOUNT "StartOnMount" -#define LAUNCH_JOBKEY_SOFTRESOURCELIMITS "SoftResourceLimits" -#define LAUNCH_JOBKEY_HARDRESOURCELIMITS "HardResourceLimits" -#define LAUNCH_JOBKEY_STANDARDINPATH "StandardInPath" -#define LAUNCH_JOBKEY_STANDARDOUTPATH "StandardOutPath" -#define LAUNCH_JOBKEY_STANDARDERRORPATH "StandardErrorPath" -#define LAUNCH_JOBKEY_DEBUG "Debug" -#define LAUNCH_JOBKEY_WAITFORDEBUGGER "WaitForDebugger" -#define LAUNCH_JOBKEY_QUEUEDIRECTORIES "QueueDirectories" -#define LAUNCH_JOBKEY_WATCHPATHS "WatchPaths" -#define LAUNCH_JOBKEY_STARTINTERVAL "StartInterval" -#define LAUNCH_JOBKEY_STARTCALENDARINTERVAL "StartCalendarInterval" -#define LAUNCH_JOBKEY_BONJOURFDS "BonjourFDs" -#define LAUNCH_JOBKEY_LASTEXITSTATUS "LastExitStatus" -#define LAUNCH_JOBKEY_PID "PID" -#define LAUNCH_JOBKEY_THROTTLEINTERVAL "ThrottleInterval" -#define LAUNCH_JOBKEY_LAUNCHONLYONCE "LaunchOnlyOnce" -#define LAUNCH_JOBKEY_ABANDONPROCESSGROUP "AbandonProcessGroup" -#define LAUNCH_JOBKEY_IGNOREPROCESSGROUPATSHUTDOWN "IgnoreProcessGroupAtShutdown" -#define LAUNCH_JOBKEY_POLICIES "Policies" -#define LAUNCH_JOBKEY_ENABLETRANSACTIONS "EnableTransactions" - -#define LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS "DenyCreatingOtherJobs" - -#define LAUNCH_JOBINETDCOMPATIBILITY_WAIT "Wait" - -#define LAUNCH_JOBKEY_MACH_RESETATCLOSE "ResetAtClose" -#define LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN "HideUntilCheckIn" -#define LAUNCH_JOBKEY_MACH_DRAINMESSAGESONCRASH "DrainMessagesOnCrash" -#define LAUNCH_JOBKEY_MACH_PINGEVENTUPDATES "PingEventUpdates" - -#define LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT "SuccessfulExit" -#define LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE "NetworkState" -#define LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE "PathState" -#define LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE "OtherJobActive" -#define LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED "OtherJobEnabled" -#define LAUNCH_JOBKEY_KEEPALIVE_AFTERINITIALDEMAND "AfterInitialDemand" -#define LAUNCH_JOBKEY_KEEPALIVE_CRASHED "Crashed" - -#define LAUNCH_JOBKEY_LAUNCHEVENTS "LaunchEvents" - -#define LAUNCH_JOBKEY_CAL_MINUTE "Minute" -#define LAUNCH_JOBKEY_CAL_HOUR "Hour" -#define LAUNCH_JOBKEY_CAL_DAY "Day" -#define LAUNCH_JOBKEY_CAL_WEEKDAY "Weekday" -#define LAUNCH_JOBKEY_CAL_MONTH "Month" - -#define LAUNCH_JOBKEY_RESOURCELIMIT_CORE "Core" -#define LAUNCH_JOBKEY_RESOURCELIMIT_CPU "CPU" -#define LAUNCH_JOBKEY_RESOURCELIMIT_DATA "Data" -#define LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE "FileSize" -#define LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK "MemoryLock" -#define LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE "NumberOfFiles" -#define LAUNCH_JOBKEY_RESOURCELIMIT_NPROC "NumberOfProcesses" -#define LAUNCH_JOBKEY_RESOURCELIMIT_RSS "ResidentSetSize" -#define LAUNCH_JOBKEY_RESOURCELIMIT_STACK "Stack" - -#define LAUNCH_JOBKEY_DISABLED_MACHINETYPE "MachineType" -#define LAUNCH_JOBKEY_DISABLED_MODELNAME "ModelName" - -#define LAUNCH_JOBSOCKETKEY_TYPE "SockType" -#define LAUNCH_JOBSOCKETKEY_PASSIVE "SockPassive" -#define LAUNCH_JOBSOCKETKEY_BONJOUR "Bonjour" -#define LAUNCH_JOBSOCKETKEY_SECUREWITHKEY "SecureSocketWithKey" -#define LAUNCH_JOBSOCKETKEY_PATHNAME "SockPathName" -#define LAUNCH_JOBSOCKETKEY_PATHMODE "SockPathMode" -#define LAUNCH_JOBSOCKETKEY_NODENAME "SockNodeName" -#define LAUNCH_JOBSOCKETKEY_SERVICENAME "SockServiceName" -#define LAUNCH_JOBSOCKETKEY_FAMILY "SockFamily" -#define LAUNCH_JOBSOCKETKEY_PROTOCOL "SockProtocol" -#define LAUNCH_JOBSOCKETKEY_MULTICASTGROUP "MulticastGroup" - -typedef struct _launch_data *launch_data_t; - -typedef enum { - LAUNCH_DATA_DICTIONARY = 1, - LAUNCH_DATA_ARRAY, - LAUNCH_DATA_FD, - LAUNCH_DATA_INTEGER, - LAUNCH_DATA_REAL, - LAUNCH_DATA_BOOL, - LAUNCH_DATA_STRING, - LAUNCH_DATA_OPAQUE, - LAUNCH_DATA_ERRNO, - LAUNCH_DATA_MACHPORT, -} launch_data_type_t; - -launch_data_t launch_data_alloc(launch_data_type_t) __ld_allocator; -launch_data_t launch_data_copy(launch_data_t) __ld_allocator; -launch_data_type_t launch_data_get_type(const launch_data_t) __ld_getter; -void launch_data_free(launch_data_t) __ld_setter; - -/* Generic Dictionaries */ -/* the value should not be changed while iterating */ -bool launch_data_dict_insert(launch_data_t, const launch_data_t, const char *) __ld_setter; -launch_data_t launch_data_dict_lookup(const launch_data_t, const char *) __ld_getter; -bool launch_data_dict_remove(launch_data_t, const char *) __ld_setter; -void launch_data_dict_iterate(const launch_data_t, void (*)(const launch_data_t, const char *, void *), void *) __ld_iterator(1, 2); -size_t launch_data_dict_get_count(const launch_data_t) __ld_getter; - -/* Generic Arrays */ -bool launch_data_array_set_index(launch_data_t, const launch_data_t, size_t) __ld_setter; -launch_data_t launch_data_array_get_index(const launch_data_t, size_t) __ld_getter; -size_t launch_data_array_get_count(const launch_data_t) __ld_getter; - -launch_data_t launch_data_new_fd(int) __ld_allocator; -launch_data_t launch_data_new_machport(mach_port_t) __ld_allocator; -launch_data_t launch_data_new_integer(long long) __ld_allocator; -launch_data_t launch_data_new_bool(bool) __ld_allocator; -launch_data_t launch_data_new_real(double) __ld_allocator; -launch_data_t launch_data_new_string(const char *) __ld_allocator; -launch_data_t launch_data_new_opaque(const void *, size_t) __ld_allocator; - -bool launch_data_set_fd(launch_data_t, int) __ld_setter; -bool launch_data_set_machport(launch_data_t, mach_port_t) __ld_setter; -bool launch_data_set_integer(launch_data_t, long long) __ld_setter; -bool launch_data_set_bool(launch_data_t, bool) __ld_setter; -bool launch_data_set_real(launch_data_t, double) __ld_setter; -bool launch_data_set_string(launch_data_t, const char *) __ld_setter; -bool launch_data_set_opaque(launch_data_t, const void *, size_t) __ld_setter; - -int launch_data_get_fd(const launch_data_t) __ld_getter; -mach_port_t launch_data_get_machport(const launch_data_t) __ld_getter; -long long launch_data_get_integer(const launch_data_t) __ld_getter; -bool launch_data_get_bool(const launch_data_t) __ld_getter; -double launch_data_get_real(const launch_data_t) __ld_getter; -const char * launch_data_get_string(const launch_data_t) __ld_getter; -void * launch_data_get_opaque(const launch_data_t) __ld_getter; -size_t launch_data_get_opaque_size(const launch_data_t) __ld_getter; -int launch_data_get_errno(const launch_data_t) __ld_getter; - - -/* launch_get_fd() - * - * Use this to get the FD if you're doing asynchronous I/O with select(), - * poll() or kevent(). - */ -int launch_get_fd(void) __ld_normal; - -/* launch_msg() - * - * Use this API to send and receive messages. - * Calling launch_msg() with no message to send is a valid way to get - * asynchronously received messages. - * - * If a message was to be sent, it returns NULL and errno on failure. - * - * If no messages were to be sent, it returns NULL and errno is set to zero if - * no more asynchronous messages are available. - */ -launch_data_t launch_msg(const launch_data_t) __ld_normal; - -__END_DECLS - -#pragma GCC visibility pop - -#endif diff --git a/launchd/src/launch_priv.h b/launchd/src/launch_priv.h deleted file mode 100644 index ee34c83..0000000 --- a/launchd/src/launch_priv.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_APACHE_LICENSE_HEADER_START@ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @APPLE_APACHE_LICENSE_HEADER_END@ - */ -#ifndef _LAUNCH_PRIV_H_ -#define _LAUNCH_PRIV_H_ - -#include -#include -#include -#include -#include -#include - -#pragma GCC visibility push(default) - -__BEGIN_DECLS - -#define LAUNCH_KEY_SETUSERENVIRONMENT "SetUserEnvironment" -#define LAUNCH_KEY_UNSETUSERENVIRONMENT "UnsetUserEnvironment" -#define LAUNCH_KEY_SHUTDOWN "Shutdown" -#define LAUNCH_KEY_SINGLEUSER "SingleUser" -#define LAUNCH_KEY_GETRESOURCELIMITS "GetResourceLimits" -#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" -#define LAUNCHD_TRUSTED_FD_ENV "__LAUNCHD_FD" -#define LAUNCHD_ASYNC_MSG_KEY "_AsyncMessage" -#define LAUNCH_KEY_BATCHCONTROL "BatchControl" -#define LAUNCH_KEY_BATCHQUERY "BatchQuery" -#define LAUNCHD_DO_APPLE_INTERNAL_LOGGING "__DoAppleInternalLogging__" - -#define LAUNCH_JOBKEY_TRANSACTIONCOUNT "TransactionCount" -#define LAUNCH_JOBKEY_QUARANTINEDATA "QuarantineData" -#define LAUNCH_JOBKEY_SANDBOXPROFILE "SandboxProfile" -#define LAUNCH_JOBKEY_SANDBOXFLAGS "SandboxFlags" -#define LAUNCH_JOBKEY_SANDBOX_NAMED "Named" -#define LAUNCH_JOBKEY_JETSAMPROPERTIES "JetsamProperties" -#define LAUNCH_JOBKEY_JETSAMPRIORITY "JetsamPriority" -#define LAUNCH_JOBKEY_JETSAMMEMORYLIMIT "JetsamMemoryLimit" -#define LAUNCH_JOBKEY_SECURITYSESSIONUUID "SecuritySessionUUID" -#define LAUNCH_JOBKEY_DISABLEASLR "DisableASLR" -#define LAUNCH_JOBKEY_XPCDOMAIN "XPCDomain" -#define LAUNCH_JOBKEY_POSIXSPAWNTYPE "POSIXSpawnType" - -#define LAUNCH_KEY_JETSAMLABEL "JetsamLabel" -#define LAUNCH_KEY_JETSAMFRONTMOST "JetsamFrontmost" -#define LAUNCH_KEY_JETSAMPRIORITY LAUNCH_JOBKEY_JETSAMPRIORITY -#define LAUNCH_KEY_JETSAMMEMORYLIMIT LAUNCH_JOBKEY_JETSAMMEMORYLIMIT - -#define LAUNCH_KEY_POSIXSPAWNTYPE_TALAPP "TALApp" -#define LAUNCH_KEY_POSIXSPAWNTYPE_WIDGET "Widget" -#define LAUNCH_KEY_POSIXSPAWNTYPE_IOSAPP "iOSApp" - -#define LAUNCH_JOBKEY_EMBEDDEDPRIVILEGEDISPENSATION "EmbeddedPrivilegeDispensation" -#define LAUNCH_JOBKEY_EMBEDDEDMAINTHREADPRIORITY "EmbeddedMainThreadPriority" - -#define LAUNCH_JOBKEY_ENTERKERNELDEBUGGERBEFOREKILL "EnterKernelDebuggerBeforeKill" -#define LAUNCH_JOBKEY_PERJOBMACHSERVICES "PerJobMachServices" -#define LAUNCH_JOBKEY_SERVICEIPC "ServiceIPC" -#define LAUNCH_JOBKEY_BINARYORDERPREFERENCE "BinaryOrderPreference" -#define LAUNCH_JOBKEY_MACHEXCEPTIONHANDLER "MachExceptionHandler" -#define LAUNCH_JOBKEY_MULTIPLEINSTANCES "MultipleInstances" -#define LAUNCH_JOBKEY_EVENTMONITOR "EventMonitor" -#define LAUNCH_JOBKEY_SHUTDOWNMONITOR "ShutdownMonitor" -#define LAUNCH_JOBKEY_BEGINTRANSACTIONATSHUTDOWN "BeginTransactionAtShutdown" -#define LAUNCH_JOBKEY_XPCDOMAINBOOTSTRAPPER "XPCDomainBootstrapper" - -#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_ENV_INSTANCEID "LaunchInstanceID" - -/* For LoginWindow. - * - * After this call, the task's bootstrap port is set to the per session launchd. - * - * This returns 1 on success (it used to return otherwise), and -1 on failure. - */ -#define LOAD_ONLY_SAFEMODE_LAUNCHAGENTS 1 << 0 -#define LAUNCH_GLOBAL_ON_DEMAND 1 << 1 -pid_t create_and_switch_to_per_session_launchd(const char * /* loginname */, int flags, ...); - -/* Also for LoginWindow. - * - * This is will load jobs at the LoginWindow prompt. - */ -void load_launchd_jobs_at_loginwindow_prompt(int flags, ...); - -/* For CoreProcesses - */ - -#define SPAWN_VIA_LAUNCHD_STOPPED 0x0001 -#define SPAWN_VIA_LAUNCHD_TALAPP 0x0002 -#define SPAWN_VIA_LAUNCHD_WIDGET 0x0004 -#define SPAWN_VIA_LAUNCHD_DISABLE_ASLR 0x0008 - -struct spawn_via_launchd_attr { - uint64_t spawn_flags; - const char * spawn_path; - const char * spawn_chdir; - const char *const * spawn_env; - const mode_t * spawn_umask; - mach_port_t * spawn_observer_port; - const cpu_type_t * spawn_binpref; - size_t spawn_binpref_cnt; - void * spawn_quarantine; - const char * spawn_seatbelt_profile; - const uint64_t * spawn_seatbelt_flags; -}; - -#define spawn_via_launchd(a, b, c) _spawn_via_launchd(a, b, c, 3) -pid_t _spawn_via_launchd( - const char *label, - const char *const *argv, - const struct spawn_via_launchd_attr *spawn_attrs, - int struct_version); - -int launch_wait(mach_port_t port); - -kern_return_t mpm_wait(mach_port_t ajob, int *wstatus); - -kern_return_t mpm_uncork_fork(mach_port_t ajob); - -__END_DECLS - -#pragma GCC visibility pop - - -#endif diff --git a/launchd/src/launchd_runtime.h b/launchd/src/launchd_runtime.h deleted file mode 100644 index dc52474..0000000 --- a/launchd/src/launchd_runtime.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_APACHE_LICENSE_HEADER_START@ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @APPLE_APACHE_LICENSE_HEADER_END@ - */ -#ifndef __LAUNCHD_RUNTIME_H__ -#define __LAUNCHD_RUNTIME_H__ - -#include -#include -#include -#include -#include -#include -#include - -#include "launchd_runtime_kill.h" -#include "launchd_ktrace.h" - -#if 0 - -/* I need to do more testing of these macros */ - -#define min_of_type(x) \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), long double), LDBL_MIN, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), double), DBL_MIN, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), float), FLT_MIN, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char), 0, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), signed char), INT8_MIN, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), short), INT16_MIN, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int), INT32_MIN, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), long), (__builtin_choose_expr(sizeof(x) == 4, INT32_MIN, INT64_MIN)), \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), long long), INT64_MIN, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), unsigned char), 0, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), unsigned short), 0, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), unsigned int), 0, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), unsigned long), 0, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), unsigned long long), 0, \ - (void)0)))))))))))))) - -#define max_of_type(x) \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), long double), LDBL_MAX, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), double), DBL_MAX, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), float), FLT_MAX, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char), UINT8_MAX, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), signed char), INT8_MAX, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), short), INT16_MIN, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int), INT32_MAX, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), long), (__builtin_choose_expr(sizeof(x) == 4, INT32_MAX, INT64_MAX)), \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), long long), INT64_MAX, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), unsigned char), UINT8_MAX, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), unsigned short), UINT16_MAX, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), unsigned int), UINT32_MAX, \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), unsigned long), (__builtin_choose_expr(sizeof(x) == 4, UINT32_MAX, UINT64_MAX)), \ - __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), unsigned long long), UINT64_MAX, \ - (void)0)))))))))))))) - -#endif - -#define likely(x) __builtin_expect((bool)(x), true) -#define unlikely(x) __builtin_expect((bool)(x), false) - -struct ldcred { - uid_t euid; - uid_t uid; - gid_t egid; - gid_t gid; - pid_t pid; - au_asid_t asid; - mach_port_t asport; -}; - -/* - * Use launchd_assumes() when we can recover, even if it means we leak or limp along. - * - * Use launchd_assert() for core initialization routines. - */ -#define launchd_assumes(e) \ - (unlikely(!(e)) ? _log_launchd_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true) - -#define launchd_assert(e) if (__builtin_constant_p(e)) { char __compile_time_assert__[e ? 1 : -1] __attribute__((unused)); } else if (!launchd_assumes(e)) { abort(); } - -void _log_launchd_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test); - -typedef void (*kq_callback)(void *, struct kevent *); -typedef boolean_t (*mig_callback)(mach_msg_header_t *, mach_msg_header_t *); -typedef void (*timeout_callback)(void); - -extern bool pid1_magic; -extern bool low_level_debug; -extern char g_username[128]; -extern char g_my_label[128]; -extern bool g_shutdown_debugging; -extern bool g_verbose_boot; -extern bool g_use_gmalloc; -extern bool g_malloc_log_stacks; -extern bool g_log_per_user_shutdown; -extern bool g_log_strict_usage; -extern bool g_embedded_shutdown_log; -extern bool g_runtime_busy_time; -extern bool g_trap_sigkill_bugs; -extern size_t runtime_busy_cnt; -extern int32_t g_sync_frequency; -extern pid_t g_wsp; - -mach_port_t runtime_get_kernel_port(void); -extern boolean_t launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply); - -void runtime_add_ref(void); -void runtime_del_ref(void); -void runtime_add_weak_ref(void); -void runtime_del_weak_ref(void); -void runtime_install_timer(void); -void runtime_remove_timer(void); - -void launchd_runtime_init(void); -void launchd_runtime_init2(void); -void launchd_runtime(void) __attribute__((noreturn)); - -void launchd_log_vm_stats(void); - -int runtime_close(int fd); -int runtime_fsync(int fd); - -#define RUNTIME_ADVISABLE_IDLE_TIMEOUT 30 - -void runtime_set_timeout(timeout_callback to_cb, unsigned int sec); -kern_return_t runtime_add_mport(mach_port_t name, mig_callback demux, mach_msg_size_t msg_size); -kern_return_t runtime_remove_mport(mach_port_t name); -struct ldcred *runtime_get_caller_creds(void); - -const char *signal_to_C_name(unsigned int sig); -const char *reboot_flags_to_C_names(unsigned int flags); - -int kevent_bulk_mod(struct kevent *kev, size_t kev_cnt); -int kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata); -void log_kevent_struct(int level, struct kevent *kev_base, int indx); - -pid_t runtime_fork(mach_port_t bsport); - -mach_msg_return_t launchd_exc_runtime_once(mach_port_t port, mach_msg_size_t rcv_msg_size, mach_msg_size_t send_msg_size, mig_reply_error_t *bufRequest, mig_reply_error_t *bufReply, mach_msg_timeout_t to); - -kern_return_t runtime_log_forward(uid_t forward_uid, gid_t forward_gid, vm_offset_t inval, mach_msg_type_number_t invalCnt); -kern_return_t runtime_log_drain(mach_port_t srp, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt); - -#define LOG_APPLEONLY 0x4141504c /* AAPL in hex */ -#define LOG_SCOLDING 0x3030493b -#define LOG_CONSOLE (1 << 31) - -struct runtime_syslog_attr { - const char *from_name; - const char *about_name; - const char *session_name; - int priority; - uid_t from_uid; - pid_t from_pid; - pid_t about_pid; -}; - -int runtime_setlogmask(int maskpri); -void runtime_closelog(void); -void runtime_syslog(int pri, const char *message, ...) __attribute__((format(printf, 2, 3))); -void runtime_vsyslog(struct runtime_syslog_attr *attr, const char *message, va_list args) __attribute__((format(printf, 2, 0))); -void runtime_log_push(void); - -int64_t runtime_get_wall_time(void) __attribute__((warn_unused_result)); -uint64_t runtime_get_opaque_time(void) __attribute__((warn_unused_result)); -uint64_t runtime_get_opaque_time_of_event(void) __attribute__((pure, warn_unused_result)); -uint64_t runtime_opaque_time_to_nano(uint64_t o) __attribute__((const, warn_unused_result)); -uint64_t runtime_get_nanoseconds_since(uint64_t o) __attribute__((pure, warn_unused_result)); - -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); -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); - -#endif diff --git a/launchd/testing/EVFILT_TIMER.c b/launchd/testing/EVFILT_TIMER.c deleted file mode 100644 index 5248bdb..0000000 --- a/launchd/testing/EVFILT_TIMER.c +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -static int kq = -1; - -time_t -now_plus_n(int n) -{ - time_t later = time(NULL); - - return later + n; -} - -void -add_abs_timer(int n) -{ - struct kevent kev; - - EV_SET(&kev, n * 100000, EVFILT_TIMER, EV_ADD, NOTE_SECONDS | NOTE_ABSOLUTE, now_plus_n(n), (void *)n); - - assert(kevent(kq, &kev, 1, NULL, 0, NULL) == 0); -} - -int -main(void) -{ - struct kevent kev; - - assert((kq = kqueue()) != -1); - - add_abs_timer(2); - add_abs_timer(3); - add_abs_timer(4); - add_abs_timer(5); - add_abs_timer(6); - - for (;;) { - assert(kevent(kq, NULL, 0, &kev, 1, NULL) == 1); - fprintf(stdout, "kev.ident == %ld kev.udata == %p\n", kev.ident, kev.udata); - add_abs_timer((int)kev.udata); - } - - exit(EXIT_SUCCESS); -} diff --git a/launchd/testing/StartCalendarInterval.plist b/launchd/testing/StartCalendarInterval.plist deleted file mode 100644 index 503a4b3..0000000 --- a/launchd/testing/StartCalendarInterval.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Label - com.apple.launchd.test.StartCalendarInterval - ProgramArguments - - echo - StartCalendarInterval - - StartCalendarInterval - - - - diff --git a/launchd/testing/StartInterval.plist b/launchd/testing/StartInterval.plist deleted file mode 100644 index bbaa92c..0000000 --- a/launchd/testing/StartInterval.plist +++ /dev/null @@ -1,15 +0,0 @@ - - - - - Label - com.apple.launchd.test.StartInterval - ProgramArguments - - echo - StartInterval - - StartInterval - 3 - - diff --git a/launchd/testing/badexec.plist b/launchd/testing/badexec.plist deleted file mode 100644 index 652809e..0000000 --- a/launchd/testing/badexec.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - Label - com.apple.launchd.test.badexec - ProgramArguments - - / - - OnDemand - - - diff --git a/launchd/testing/badexit.plist b/launchd/testing/badexit.plist deleted file mode 100644 index ea84c23..0000000 --- a/launchd/testing/badexit.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - Label - com.apple.launchd.test.badexit - ProgramArguments - - false - - OnDemand - - - diff --git a/launchd/testing/loginwindow_helper_code.c b/launchd/testing/loginwindow_helper_code.c deleted file mode 100644 index ee529e9..0000000 --- a/launchd/testing/loginwindow_helper_code.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main(void) -{ - mach_port_t oldbsport = bootstrap_port; - pid_t p; - - if (getuid() != 0) { - fprintf(stderr, "This test tool must be run as root.\n"); - exit(EXIT_FAILURE); - } - - p = create_and_switch_to_per_session_launchd("www"); - - if (p > 0) { - fprintf(stdout, "Our PID: %d\n", getpid()); - fprintf(stdout, "Per session launchd PID: %d\n", p); - fprintf(stdout, "Old bootstrap port: 0x%x\n", oldbsport); - fprintf(stdout, "New bootstrap port: 0x%x\n", bootstrap_port); - for (;;) { - pause(); - } - } else if (p == -1) { - fprintf(stderr, "create_and_switch_to_per_session_launchd() failed: %s\n", strerror(errno)); - } else if (p == 0) { - fprintf(stderr, "create_and_switch_to_per_session_launchd() returned zero?!?\n"); - } - exit(EXIT_FAILURE); -} diff --git a/launchd/testing/mach_via_launchd.c b/launchd/testing/mach_via_launchd.c deleted file mode 100644 index 851db4b..0000000 --- a/launchd/testing/mach_via_launchd.c +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include -#include -#include -#include -#include - -void print_mach_service(launch_data_t obj, const char *key, void *context) -{ - if (launch_data_get_type(obj) == LAUNCH_DATA_MACHPORT) { - fprintf(stdout, "%s: %d\n", key, launch_data_get_machport(obj)); - mach_port_deallocate(mach_task_self(), launch_data_get_machport(obj)); - mach_port_mod_refs(mach_task_self(), launch_data_get_machport(obj), MACH_PORT_RIGHT_RECEIVE, -1); - } else { - fprintf(stdout, "%s: not a mach port\n", key); - } -} - -int main(void) -{ - launch_data_t resp, tmp, msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); - - resp = launch_msg(msg); - - if (resp == NULL) { - fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { - errno = launch_data_get_errno(resp); - fprintf(stderr, "launch_msg() response: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_MACHSERVICES); - - if (tmp == NULL) { - fprintf(stderr, "no mach services found!\n"); - exit(EXIT_FAILURE); - } - - launch_data_dict_iterate(tmp, print_mach_service, NULL); - - sleep(1); - exit(EXIT_SUCCESS); -} diff --git a/launchd/testing/mach_via_launchd.plist b/launchd/testing/mach_via_launchd.plist deleted file mode 100644 index e92df3e..0000000 --- a/launchd/testing/mach_via_launchd.plist +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Label - launchd.testing.mach_via_launchd - ProgramArguments - - ./mach_via_launchd - - OnDemand - - MachServices - - com.apple.launchd.test.service - - - - diff --git a/launchd/testing/missed-EVFILT_READ.c b/launchd/testing/missed-EVFILT_READ.c deleted file mode 100644 index 042a551..0000000 --- a/launchd/testing/missed-EVFILT_READ.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * EVFILT_READ doesn't fire reliably - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void do_parent(int thefd); -static void do_child(int thefd); - -int main(void) -{ - int sp[2]; - - if (-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) { - fprintf(stderr, "socketpair(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - switch (fork()) { - case -1: - fprintf(stderr, "fork(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - case 0: - close(sp[0]); - do_child(sp[1]); - break; - default: - close(sp[1]); - do_parent(sp[0]); - break; - } - - exit(EXIT_SUCCESS); -} - -void -do_child(int thefd) -{ - char junk = '\0'; - - for (;;) { - if (-1 == write(thefd, &junk, sizeof(junk))) { - fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - } - if (-1 == read(thefd, &junk, sizeof(junk))) { - fprintf(stderr, "%d: read(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - } - } -} - -void -do_parent(int thefd) -{ - struct timespec timeout = { 5, 0 }; - int iter = 0, kq; - struct kevent kev; - char junk = '\0'; - - if (-1 == (kq = kqueue())) { - fprintf(stderr, "kqueue(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - EV_SET(&kev, thefd, EVFILT_READ, EV_ADD, 0, 0, NULL); - - if (-1 == kevent(kq, &kev, 1, NULL, 0, NULL)) { - fprintf(stderr, "%d: kevent(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - } - - for (;;) { - switch (kevent(kq, NULL, 0, &kev, 1, &timeout)) { - case -1: - fprintf(stderr, "%d: kevent(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - case 0: - fprintf(stderr, "After %d iterations, 4437060 still exists!\n", iter); - exit(EXIT_FAILURE); - case 1: - break; - default: - fprintf(stderr, "kevent should only return -1, 0 or 1 for this case!\n"); - exit(EXIT_FAILURE); - } - - if (kev.filter != EVFILT_READ) { - fprintf(stderr, "kevent should return EVFILT_READ!\n"); - exit(EXIT_FAILURE); - } - - if (-1 == read(thefd, &junk, sizeof(junk))) { - fprintf(stderr, "read(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - if (-1 == write(thefd, &junk, sizeof(junk))) { - fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - } - iter++; - } -} diff --git a/launchd/testing/missed-EVFILT_TIMER.c b/launchd/testing/missed-EVFILT_TIMER.c deleted file mode 100644 index 117d49c..0000000 --- a/launchd/testing/missed-EVFILT_TIMER.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include -#include -#include - -void -test5225889(int first, int second) -{ - struct timeval tvs, tve, tvd; - struct timespec timeout = { 30, 0 }; - struct kevent kev; - int r, kq = kqueue(); - - fprintf(stdout, "First timer %i being updated to %i.\n", first, second); - - assert(kq != -1); - - EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, first, NULL); - r = kevent(kq, &kev, 1, NULL, 0, NULL); - assert(r != -1); - - EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, second, NULL); - r = kevent(kq, &kev, 1, NULL, 0, NULL); - assert(r != -1); - - gettimeofday(&tvs, NULL); - r = kevent(kq, NULL, 0, &kev, 1, &timeout); - gettimeofday(&tve, NULL); - - timersub(&tve, &tvs, &tvd); - - fprintf(stdout, "Waited %lu seconds for kevent() to return.\n", tvd.tv_sec); - - switch (r) { - case 1: - assert(kev.data == second); - assert(tvd.tv_sec >= second); - break; - case -1: - case 0: - default: - fprintf(stderr, "Bug 5225889 still exists!\n"); - exit(EXIT_FAILURE); - } -} - -int -main(void) -{ - test5225889(5, 10); - test5225889(10, 5); - - fprintf(stdout, "Finished.\n"); - - exit(EXIT_SUCCESS); -} diff --git a/launchd/testing/missed-EVFILT_WRITE.c b/launchd/testing/missed-EVFILT_WRITE.c deleted file mode 100644 index 2b346d7..0000000 --- a/launchd/testing/missed-EVFILT_WRITE.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Lots of hangs in GetLaunchDaemonService state - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void do_parent(int thefd); -static void do_child(int thefd); - -int main(void) -{ - int sp[2]; - - if (-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) { - fprintf(stderr, "socketpair(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - switch (fork()) { - case -1: - fprintf(stderr, "fork(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - case 0: - close(sp[0]); - do_child(sp[1]); - break; - default: - close(sp[1]); - do_parent(sp[0]); - break; - } - - exit(EXIT_SUCCESS); -} - -static void do_child(int thefd) -{ - char buf[500000]; - fd_set rfds; - int r, readhunks = 2; - - if (-1 == fcntl(thefd, F_SETFL, O_NONBLOCK)) { - fprintf(stderr, "fcntl(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - for (;;) { - if (2 == readhunks) { - if (-1 == write(thefd, buf, 1)) { - fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - } - readhunks = 0; - } - r = read(thefd, buf, sizeof(buf)); - - if (-1 == r && errno != EAGAIN) { - fprintf(stderr, "%d: read(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - } - - if (r > 0) - readhunks++; - - if (readhunks == 2) - continue; - - FD_ZERO(&rfds); - FD_SET(thefd, &rfds); - - select(thefd + 1, &rfds, NULL, NULL, NULL); - } -} - -static void do_parent(int thefd) -{ - struct timespec timeout = { 1, 0 }; - char buf[500000]; - struct kevent kev; - int iter = 0, kq = kqueue(); - - if (-1 == (kq = kqueue())) { - fprintf(stderr, "kqueue(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - if (-1 == fcntl(thefd, F_SETFL, O_NONBLOCK)) { - fprintf(stderr, "fcntl(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - EV_SET(&kev, thefd, EVFILT_READ, EV_ADD, 0, 0, NULL); - - if (-1 == kevent(kq, &kev, 1, NULL, 0, NULL)) { - fprintf(stderr, "%d: kevent(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - } - - for (;;) { - switch (kevent(kq, NULL, 0, &kev, 1, &timeout)) { - case -1: - fprintf(stderr, "%d: kevent(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - case 0: - fprintf(stderr, "After %d iterations, 4038866 still exists!\n", iter); - exit(EXIT_FAILURE); - case 1: - break; - default: - fprintf(stderr, "kevent should only return -1, 0 or 1 for this case!\n"); - exit(EXIT_FAILURE); - } - - switch (kev.filter) { - case EVFILT_READ: - if (-1 == read(thefd, buf, sizeof(buf))) { - fprintf(stderr, "read(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - if (-1 == write(thefd, buf, sizeof(buf))) { - fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - } - EV_SET(&kev, thefd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); - if (-1 == kevent(kq, &kev, 1, NULL, 0, NULL)) { - fprintf(stderr, "%d: kevent(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - } - break; - case EVFILT_WRITE: - if (-1 == write(thefd, buf, 1)) { - fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - } - EV_SET(&kev, thefd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); - if (-1 == kevent(kq, &kev, 1, NULL, 0, NULL)) { - fprintf(stderr, "%d: kevent(): %s\n", __LINE__, strerror(errno)); - exit(EXIT_FAILURE); - } - break; - default: - fprintf(stderr, "kevent filter isn't EVFILT_READ or EVFILT_WRITE!\n"); - exit(EXIT_FAILURE); - } - iter++; - } -} diff --git a/launchd/testing/missed-fds.c b/launchd/testing/missed-fds.c deleted file mode 100644 index 24577b5..0000000 --- a/launchd/testing/missed-fds.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 8G1153: Cannot SSH into machine despite Remote Login being checked - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void do_parent(int thefd); -static void do_child(int thefd); - -int main(void) -{ - int sp[2]; - pid_t p; - - assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != -1); - - assert((p = fork()) != -1); - - if (p == 0) { - assert(close(sp[0]) != -1); - do_child(sp[1]); - } else { - assert(close(sp[1]) != -1); - do_parent(sp[0]); - } - - exit(EXIT_SUCCESS); -} - -static int -alloc_random_fd(void) -{ - struct sockaddr_in ina; - int fd; - - memset(&ina, 0, sizeof(ina)); - ina.sin_family = AF_INET; - assert((fd = socket(PF_INET, SOCK_STREAM, 0)) != -1); - assert(bind(fd, (struct sockaddr *)&ina, sizeof(ina)) != -1); - assert(listen(fd, SOMAXCONN) != -1); - - return fd; -} - -static int total_fds_sent = 0; - -void -send_fds(int thefd) -{ - struct cmsghdr *cm = NULL; - struct msghdr mh; - struct iovec iov; - size_t sentctrllen = 0; - int fdcnt = (rand() % 223) + 1; /* 223 is prime */ - int r, i, fds[fdcnt]; - - memset(&mh, 0, sizeof(mh)); - - iov.iov_base = &fdcnt; - iov.iov_len = sizeof(fdcnt); - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - - for (i = 0; i < fdcnt; i++) { - fds[i] = alloc_random_fd(); - } - - sentctrllen = mh.msg_controllen = CMSG_SPACE(fdcnt * sizeof(int)); - - mh.msg_control = cm = alloca(mh.msg_controllen); - - memset(cm, 0, mh.msg_controllen); - - cm->cmsg_len = CMSG_LEN(fdcnt * sizeof(int)); - cm->cmsg_level = SOL_SOCKET; - cm->cmsg_type = SCM_RIGHTS; - - memcpy(CMSG_DATA(cm), fds, fdcnt * sizeof(int)); - - if (sendmsg(thefd, &mh, 0) == -1) { - fprintf(stderr, "Child: sendmsg(): %s\n", strerror(errno)); - fprintf(stderr, "Child: Tried to send %d fds\n", fdcnt); - fprintf(stderr, "Child: Total FDs sent: %d\n", total_fds_sent); - sleep(1); - exit(EXIT_FAILURE); - } - total_fds_sent += fdcnt; - - assert(sentctrllen == mh.msg_controllen); - - r = read(thefd, &i, sizeof(i)); - assert(r != -1); - assert(r != 0); - - for (i = 0; i < fdcnt; i++) { - assert(close(fds[i]) != -1); - } -} - -void -do_child(int thefd) -{ - for (;;) { - send_fds(thefd); - } -} - -static int total_fds_received = 0; - -static bool -fetch_and_check_fds(int thefd) -{ - struct cmsghdr *cm = alloca(4096); - struct msghdr mh; - struct iovec iov; - int r, i, *fds, fdcnt = 0, sentfds; - - memset(&mh, 0, sizeof(mh)); - - iov.iov_base = &fdcnt; - iov.iov_len = sizeof(fdcnt); - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_control = cm; - mh.msg_controllen = 4096; - - r = recvmsg(thefd, &mh, 0); - assert(r != -1); - assert(r != 0); - assert(!(mh.msg_flags & MSG_CTRUNC)); - assert(mh.msg_controllen > 0); - - fds = (int *)CMSG_DATA(cm); - sentfds = (mh.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int); - - if (sentfds != fdcnt) { - fprintf(stderr, "%d FDs sent, %d actually received.\n", fdcnt, sentfds); - return false; - } - - total_fds_received += fdcnt; - - for (i = 0; i < fdcnt; i++) { - assert(close(fds[i]) != -1); - } - - r = write(thefd, &fdcnt, sizeof(fdcnt)); - assert(r != -1); - assert(r != 0); - - return true; -} - -void -do_parent(int thefd) -{ - struct kevent kev; - int kq, iter = 0; - - EV_SET(&kev, thefd, EVFILT_READ, EV_ADD, 0, 0, NULL); - - assert((kq = kqueue()) != -1); - assert(kevent(kq, &kev, 1, NULL, 0, NULL) != -1); - - for (iter = 0; ; iter++) { - assert(kevent(kq, NULL, 0, &kev, 1, NULL) == 1); - assert(kev.filter == EVFILT_READ); - if (!fetch_and_check_fds(thefd)) - break; - } - - fprintf(stderr, "After %d iterations and %d FDs received, bug 4389914 still exists!\n", iter, total_fds_received); - exit(EXIT_FAILURE); -} diff --git a/launchd/testing/missing_req_keys.plist b/launchd/testing/missing_req_keys.plist deleted file mode 100644 index 6056e7d..0000000 --- a/launchd/testing/missing_req_keys.plist +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/launchd/testing/our_kill_is_busted.c b/launchd/testing/our_kill_is_busted.c deleted file mode 100644 index 5ccda65..0000000 --- a/launchd/testing/our_kill_is_busted.c +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int -main(void) -{ - int wstatus; - pid_t p, wr; - int kill_r, killpg_r, r; - int kill_e, killpg_e; - - p = fork(); - assert(p != -1); - - if (p == 0) { - r = setsid(); - assert(r != -1); - _exit(EXIT_SUCCESS); - } - - sleep(1); - - errno = 0; - kill_r = kill(p, SIGTERM); - kill_e = errno; - - errno = 0; - killpg_r = kill(-(p), SIGTERM); - killpg_e = errno; - - if (kill_r != killpg_r) { - fprintf(stderr, "Bug. kill() is inconsistent.\n"); - fprintf(stderr, "kill( p, SIGTERM) returned %d and errno == %d\n", kill_r, kill_e); - fprintf(stderr, "kill(-(p), SIGTERM) returned %d and errno == %d\n", killpg_r, killpg_e); - if (kill_e == EPERM || killpg_e == EPERM) { - fprintf(stderr, "The kernel lied. We should have the right to kill 'p' and it returned EPERM.\n"); - } - if (kill_e == ESRCH || killpg_e == ESRCH) { - fprintf(stderr, "The kernel is confused. PID 'p' exists, but the kernel couldn't find it.\n"); - } - - exit(EXIT_FAILURE); - } - - wr = waitpid(p, &wstatus, 0); - assert(wr == p); - - exit(EXIT_SUCCESS); -} diff --git a/launchd/testing/secsock.plist b/launchd/testing/secsock.plist deleted file mode 100644 index 46e4281..0000000 --- a/launchd/testing/secsock.plist +++ /dev/null @@ -1,21 +0,0 @@ - - - - - Label - com.apple.launchd.test.secsock - ProgramArguments - - sleep - 10000 - - Sockets - - AuthSock - - SecureSocketWithKey - SSH_AUTH_SOCK - - - - diff --git a/launchd/testing/signaldeath.plist b/launchd/testing/signaldeath.plist deleted file mode 100644 index bf84a3e..0000000 --- a/launchd/testing/signaldeath.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Label - com.apple.launchd.test.signaldeath - ProgramArguments - - kill - -1 - 0 - - OnDemand - - - diff --git a/launchd/testing/spawn_via_launchd.c b/launchd/testing/spawn_via_launchd.c deleted file mode 100644 index 5df7f94..0000000 --- a/launchd/testing/spawn_via_launchd.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int -main(int argc, const char *const *argv) -{ - struct timeval tvs, tve, tvd; - mach_port_t mpo = MACH_PORT_NULL; - int wstatus; - pid_t p; - - struct spawn_via_launchd_attr attrs; - memset(&attrs, 0, sizeof(attrs)); - attrs.spawn_flags = SPAWN_VIA_LAUNCHD_STOPPED; // | SPAWN_VIA_LAUNCHD_FORCE_PPC; - attrs.spawn_observer_port = &mpo; -#if 0 - const char *const env[] = { "FOO=bar", "ROCK=roll", NULL }; - attrs.spawn_path = "/bin/booger"; - attrs.spawn_chdir = "/Users/me"; - attrs.spawn_env = env; -#endif - - gettimeofday(&tvs, NULL); - p = spawn_via_launchd("com.example.booger", argv + 1, &attrs); - - if (p == -1) { - fprintf(stderr, "spawn_via_launchd(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - kill(p, SIGCONT); - gettimeofday(&tve, NULL); - timersub(&tve, &tvs, &tvd); - fprintf(stdout, "p == %d mpo = 0x%x in %ld usec\n", p, mpo, tvd.tv_sec * 1000000 + tvd.tv_usec); - - assert(mpm_wait(mpo, &wstatus) == 0); - - fprintf(stdout, "wstatus == %d\n", wstatus); - - exit(EXIT_SUCCESS); -} diff --git a/launchd/testing/vproc_gsk_test.c b/launchd/testing/vproc_gsk_test.c deleted file mode 100644 index 5df62c0..0000000 --- a/launchd/testing/vproc_gsk_test.c +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include -#include -#include - -int main(void) -{ - int64_t val; - bool is_native; - pid_t p; - uid_t u; - - /* we assign val to p or u due to 64 bit to 32 bit trucation */ - - assert(vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, &val) == NULL); - p = val; - - assert(vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, &val) == NULL); - u = val; - - assert(vproc_swap_integer(NULL, VPROC_GSK_IS_NATIVE, NULL, &val) == NULL); - is_native = val; - - fprintf(stdout, "UID = %u PID = %u Native = %u\n", u, p, is_native); - - return 0; -} diff --git a/launchd/testing/vproc_swap_complex.c b/launchd/testing/vproc_swap_complex.c deleted file mode 100644 index 6ff8d53..0000000 --- a/launchd/testing/vproc_swap_complex.c +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -static void my_callback(const launch_data_t obj, const char *key, void *context); - -int main(void) -{ - launch_data_t output_obj = NULL; - - assert(vproc_swap_complex(NULL, VPROC_GSK_ENVIRONMENT, NULL, &output_obj) == 0); - - assert(launch_data_get_type(output_obj) == LAUNCH_DATA_DICTIONARY); - - launch_data_dict_iterate(output_obj, my_callback, stdout); - - return 0; -} - -void -my_callback(const launch_data_t obj, const char *key, void *context) -{ - fprintf(context, "%s == %s\n", key, launch_data_get_string(obj)); -} diff --git a/launchd/src/bootstrap.h b/liblaunch/bootstrap.h similarity index 99% rename from launchd/src/bootstrap.h rename to liblaunch/bootstrap.h index f09f82f..8ddb3b3 100644 --- a/launchd/src/bootstrap.h +++ b/liblaunch/bootstrap.h @@ -1,5 +1,5 @@ -#ifndef _BOOTSTRAP_H_ -#define _BOOTSTRAP_H_ +#ifndef __BOOTSTRAP_H__ +#define __BOOTSTRAP_H__ /* * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved. * @@ -355,4 +355,4 @@ const char *bootstrap_strerror(kern_return_t r) __attribute__((__nothrow__, __pu __END_DECLS -#endif +#endif /* __BOOTSTRAP_H__ */ diff --git a/launchd/src/bootstrap_priv.h b/liblaunch/bootstrap_priv.h similarity index 96% rename from launchd/src/bootstrap_priv.h rename to liblaunch/bootstrap_priv.h index 54d443e..63e1c4e 100644 --- a/launchd/src/bootstrap_priv.h +++ b/liblaunch/bootstrap_priv.h @@ -1,5 +1,3 @@ -#ifndef _BOOTSTRAP_PRIVATE_H_ -#define _BOOTSTRAP_PRIVATE_H_ /* * Copyright (c) 2007 Apple Inc. All rights reserved. * @@ -20,6 +18,9 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ +#ifndef __BOOTSTRAP_PRIVATE_H__ +#define __BOOTSTRAP_PRIVATE_H__ + #include #include #include @@ -66,4 +67,4 @@ kern_return_t bootstrap_get_root(mach_port_t bp, mach_port_t *root); __END_DECLS -#endif +#endif /* __BOOTSTRAP_PRIVATE_H__ */ diff --git a/liblaunch/launch.h b/liblaunch/launch.h new file mode 100644 index 0000000..aadc62f --- /dev/null +++ b/liblaunch/launch.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +#ifndef __LAUNCH_H__ +#define __LAUNCH_H__ + +#include +#include +#include +#include + +#pragma GCC visibility push(default) + +__BEGIN_DECLS + +#ifdef __GNUC__ +#define __ld_normal __attribute__((__nothrow__)) +#define __ld_setter __attribute__((__nothrow__, __nonnull__)) +#define __ld_getter __attribute__((__nothrow__, __nonnull__, __pure__, __warn_unused_result__)) +#define __ld_iterator(x, y) __attribute__((__nonnull__(x, y))) +#define __ld_allocator __attribute__((__nothrow__, __malloc__, __nonnull__, __warn_unused_result__)) +#else +#define __ld_normal +#define __ld_setter +#define __ld_getter +#define __ld_iterator(x, y) +#define __ld_allocator +#endif + +#define LAUNCH_KEY_SUBMITJOB "SubmitJob" +#define LAUNCH_KEY_REMOVEJOB "RemoveJob" +#define LAUNCH_KEY_STARTJOB "StartJob" +#define LAUNCH_KEY_STOPJOB "StopJob" +#define LAUNCH_KEY_GETJOB "GetJob" +#define LAUNCH_KEY_GETJOBS "GetJobs" +#define LAUNCH_KEY_CHECKIN "CheckIn" + +#define LAUNCH_JOBKEY_LABEL "Label" +#define LAUNCH_JOBKEY_DISABLED "Disabled" +#define LAUNCH_JOBKEY_USERNAME "UserName" +#define LAUNCH_JOBKEY_GROUPNAME "GroupName" +#define LAUNCH_JOBKEY_TIMEOUT "TimeOut" +#define LAUNCH_JOBKEY_EXITTIMEOUT "ExitTimeOut" +#define LAUNCH_JOBKEY_INITGROUPS "InitGroups" +#define LAUNCH_JOBKEY_SOCKETS "Sockets" +#define LAUNCH_JOBKEY_MACHSERVICES "MachServices" +#define LAUNCH_JOBKEY_MACHSERVICELOOKUPPOLICIES "MachServiceLookupPolicies" +#define LAUNCH_JOBKEY_INETDCOMPATIBILITY "inetdCompatibility" +#define LAUNCH_JOBKEY_ENABLEGLOBBING "EnableGlobbing" +#define LAUNCH_JOBKEY_PROGRAMARGUMENTS "ProgramArguments" +#define LAUNCH_JOBKEY_PROGRAM "Program" +#define LAUNCH_JOBKEY_ONDEMAND "OnDemand" +#define LAUNCH_JOBKEY_KEEPALIVE "KeepAlive" +#define LAUNCH_JOBKEY_LIMITLOADTOHOSTS "LimitLoadToHosts" +#define LAUNCH_JOBKEY_LIMITLOADFROMHOSTS "LimitLoadFromHosts" +#define LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE "LimitLoadToSessionType" +#define LAUNCH_JOBKEY_LIMITLOADTOHARDWARE "LimitLoadToHardware" +#define LAUNCH_JOBKEY_LIMITLOADFROMHARDWARE "LimitLoadFromHardware" +#define LAUNCH_JOBKEY_RUNATLOAD "RunAtLoad" +#define LAUNCH_JOBKEY_ROOTDIRECTORY "RootDirectory" +#define LAUNCH_JOBKEY_WORKINGDIRECTORY "WorkingDirectory" +#define LAUNCH_JOBKEY_ENVIRONMENTVARIABLES "EnvironmentVariables" +#define LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES "UserEnvironmentVariables" +#define LAUNCH_JOBKEY_UMASK "Umask" +#define LAUNCH_JOBKEY_NICE "Nice" +#define LAUNCH_JOBKEY_HOPEFULLYEXITSFIRST "HopefullyExitsFirst" +#define LAUNCH_JOBKEY_HOPEFULLYEXITSLAST "HopefullyExitsLast" +#define LAUNCH_JOBKEY_LOWPRIORITYIO "LowPriorityIO" +#define LAUNCH_JOBKEY_SESSIONCREATE "SessionCreate" +#define LAUNCH_JOBKEY_STARTONMOUNT "StartOnMount" +#define LAUNCH_JOBKEY_SOFTRESOURCELIMITS "SoftResourceLimits" +#define LAUNCH_JOBKEY_HARDRESOURCELIMITS "HardResourceLimits" +#define LAUNCH_JOBKEY_STANDARDINPATH "StandardInPath" +#define LAUNCH_JOBKEY_STANDARDOUTPATH "StandardOutPath" +#define LAUNCH_JOBKEY_STANDARDERRORPATH "StandardErrorPath" +#define LAUNCH_JOBKEY_DEBUG "Debug" +#define LAUNCH_JOBKEY_WAITFORDEBUGGER "WaitForDebugger" +#define LAUNCH_JOBKEY_QUEUEDIRECTORIES "QueueDirectories" +#define LAUNCH_JOBKEY_WATCHPATHS "WatchPaths" +#define LAUNCH_JOBKEY_STARTINTERVAL "StartInterval" +#define LAUNCH_JOBKEY_STARTCALENDARINTERVAL "StartCalendarInterval" +#define LAUNCH_JOBKEY_BONJOURFDS "BonjourFDs" +#define LAUNCH_JOBKEY_LASTEXITSTATUS "LastExitStatus" +#define LAUNCH_JOBKEY_PID "PID" +#define LAUNCH_JOBKEY_THROTTLEINTERVAL "ThrottleInterval" +#define LAUNCH_JOBKEY_LAUNCHONLYONCE "LaunchOnlyOnce" +#define LAUNCH_JOBKEY_ABANDONPROCESSGROUP "AbandonProcessGroup" +#define LAUNCH_JOBKEY_IGNOREPROCESSGROUPATSHUTDOWN "IgnoreProcessGroupAtShutdown" +#define LAUNCH_JOBKEY_POLICIES "Policies" +#define LAUNCH_JOBKEY_ENABLETRANSACTIONS "EnableTransactions" + +#define LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS "DenyCreatingOtherJobs" + +#define LAUNCH_JOBINETDCOMPATIBILITY_WAIT "Wait" + +#define LAUNCH_JOBKEY_MACH_RESETATCLOSE "ResetAtClose" +#define LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN "HideUntilCheckIn" +#define LAUNCH_JOBKEY_MACH_DRAINMESSAGESONCRASH "DrainMessagesOnCrash" +#define LAUNCH_JOBKEY_MACH_PINGEVENTUPDATES "PingEventUpdates" + +#define LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT "SuccessfulExit" +#define LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE "NetworkState" +#define LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE "PathState" +#define LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE "OtherJobActive" +#define LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED "OtherJobEnabled" +#define LAUNCH_JOBKEY_KEEPALIVE_AFTERINITIALDEMAND "AfterInitialDemand" +#define LAUNCH_JOBKEY_KEEPALIVE_CRASHED "Crashed" + +#define LAUNCH_JOBKEY_LAUNCHEVENTS "LaunchEvents" + +#define LAUNCH_JOBKEY_CAL_MINUTE "Minute" +#define LAUNCH_JOBKEY_CAL_HOUR "Hour" +#define LAUNCH_JOBKEY_CAL_DAY "Day" +#define LAUNCH_JOBKEY_CAL_WEEKDAY "Weekday" +#define LAUNCH_JOBKEY_CAL_MONTH "Month" + +#define LAUNCH_JOBKEY_RESOURCELIMIT_CORE "Core" +#define LAUNCH_JOBKEY_RESOURCELIMIT_CPU "CPU" +#define LAUNCH_JOBKEY_RESOURCELIMIT_DATA "Data" +#define LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE "FileSize" +#define LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK "MemoryLock" +#define LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE "NumberOfFiles" +#define LAUNCH_JOBKEY_RESOURCELIMIT_NPROC "NumberOfProcesses" +#define LAUNCH_JOBKEY_RESOURCELIMIT_RSS "ResidentSetSize" +#define LAUNCH_JOBKEY_RESOURCELIMIT_STACK "Stack" + +#define LAUNCH_JOBKEY_DISABLED_MACHINETYPE "MachineType" +#define LAUNCH_JOBKEY_DISABLED_MODELNAME "ModelName" + +#define LAUNCH_JOBSOCKETKEY_TYPE "SockType" +#define LAUNCH_JOBSOCKETKEY_PASSIVE "SockPassive" +#define LAUNCH_JOBSOCKETKEY_BONJOUR "Bonjour" +#define LAUNCH_JOBSOCKETKEY_SECUREWITHKEY "SecureSocketWithKey" +#define LAUNCH_JOBSOCKETKEY_PATHNAME "SockPathName" +#define LAUNCH_JOBSOCKETKEY_PATHMODE "SockPathMode" +#define LAUNCH_JOBSOCKETKEY_NODENAME "SockNodeName" +#define LAUNCH_JOBSOCKETKEY_SERVICENAME "SockServiceName" +#define LAUNCH_JOBSOCKETKEY_FAMILY "SockFamily" +#define LAUNCH_JOBSOCKETKEY_PROTOCOL "SockProtocol" +#define LAUNCH_JOBSOCKETKEY_MULTICASTGROUP "MulticastGroup" + +/* These APIs are NOT suitable for general use. Their use should be constrained + * to checking into launchd to obtain socket file descriptors using the + * LAUNCH_CHECK_IN message type. + */ +typedef struct _launch_data *launch_data_t; + +typedef enum { + LAUNCH_DATA_DICTIONARY = 1, + LAUNCH_DATA_ARRAY, + LAUNCH_DATA_FD, + LAUNCH_DATA_INTEGER, + LAUNCH_DATA_REAL, + LAUNCH_DATA_BOOL, + LAUNCH_DATA_STRING, + LAUNCH_DATA_OPAQUE, + LAUNCH_DATA_ERRNO, + LAUNCH_DATA_MACHPORT, +} launch_data_type_t; + +__ld_allocator +launch_data_t +launch_data_alloc(launch_data_type_t); + +__ld_allocator +launch_data_t +launch_data_copy(launch_data_t); + +__ld_getter +launch_data_type_t +launch_data_get_type(const launch_data_t); + +__ld_setter +void +launch_data_free(launch_data_t); + +__ld_setter +bool +launch_data_dict_insert(launch_data_t, const launch_data_t, const char *); + +__ld_getter +launch_data_t +launch_data_dict_lookup(const launch_data_t, const char *); + +__ld_setter +bool +launch_data_dict_remove(launch_data_t, const char *); + +__ld_iterator(1, 2) +void +launch_data_dict_iterate(const launch_data_t, + void (*)(const launch_data_t, const char *, void *), void *); + +__ld_getter +size_t +launch_data_dict_get_count(const launch_data_t); + +__ld_setter +bool +launch_data_array_set_index(launch_data_t, const launch_data_t, size_t); + +__ld_getter +launch_data_t +launch_data_array_get_index(const launch_data_t, size_t); + +__ld_getter +size_t +launch_data_array_get_count(const launch_data_t); + +__ld_allocator +launch_data_t +launch_data_new_fd(int); + +__ld_allocator +launch_data_t +launch_data_new_machport(mach_port_t); + +__ld_allocator +launch_data_t +launch_data_new_integer(long long); + +__ld_allocator +launch_data_t +launch_data_new_bool(bool); + +__ld_allocator +launch_data_t +launch_data_new_real(double); + +__ld_allocator +launch_data_t +launch_data_new_string(const char *); + +__ld_allocator +launch_data_t +launch_data_new_opaque(const void *, size_t); + + __ld_setter +bool +launch_data_set_fd(launch_data_t, int); + + __ld_setter +bool +launch_data_set_machport(launch_data_t, mach_port_t); + + __ld_setter +bool +launch_data_set_integer(launch_data_t, long long); + + __ld_setter +bool +launch_data_set_bool(launch_data_t, bool); + + __ld_setter +bool +launch_data_set_real(launch_data_t, double); + + __ld_setter +bool +launch_data_set_string(launch_data_t, const char *); + + __ld_setter +bool +launch_data_set_opaque(launch_data_t, const void *, size_t); + +__ld_getter +int +launch_data_get_fd(const launch_data_t); + +__ld_getter +mach_port_t +launch_data_get_machport(const launch_data_t); + +__ld_getter +long long +launch_data_get_integer(const launch_data_t); + +__ld_getter +bool +launch_data_get_bool(const launch_data_t); + +__ld_getter +double +launch_data_get_real(const launch_data_t); + +__ld_getter +const char * +launch_data_get_string(const launch_data_t); + +__ld_getter +void * +launch_data_get_opaque(const launch_data_t); + +__ld_getter +size_t +launch_data_get_opaque_size(const launch_data_t); + +__ld_getter +int +launch_data_get_errno(const launch_data_t); + + +/* launch_get_fd() + * + * Use this to get the FD if you're doing asynchronous I/O with select(), + * poll() or kevent(). + */ +__ld_normal +int +launch_get_fd(void); + +/* launch_msg() + * + * Use this API to check in. Nothing else. + */ +__ld_normal +launch_data_t +launch_msg(const launch_data_t); + +__END_DECLS + +#pragma GCC visibility pop + +#endif /* __LAUNCH_H__ */ diff --git a/launchd/src/launch_internal.h b/liblaunch/launch_internal.h similarity index 69% rename from launchd/src/launch_internal.h rename to liblaunch/launch_internal.h index 46c1725..aa6576f 100644 --- a/launchd/src/launch_internal.h +++ b/liblaunch/launch_internal.h @@ -1,5 +1,3 @@ -#ifndef _LAUNCH_INTERNAL_H_ -#define _LAUNCH_INTERNAL_H_ /* * Copyright (c) 2007 Apple Computer, Inc. All rights reserved. * @@ -20,34 +18,15 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ -#pragma GCC visibility push(default) +#ifndef __LAUNCH_INTERNAL_H__ +#define __LAUNCH_INTERNAL_H__ + +#include -#define LAUNCHD_DB_PREFIX "/var/db/launchd.db" +#pragma GCC visibility push(default) -struct _launch_data { - uint64_t type; - union { - struct { - union { - launch_data_t *_array; - char *string; - void *opaque; - int64_t __junk; - }; - union { - uint64_t _array_cnt; - uint64_t string_len; - uint64_t opaque_size; - }; - }; - int64_t fd; - uint64_t mp; - uint64_t err; - int64_t number; - uint64_t boolean; /* We'd use 'bool' but this struct needs to be used under Rosetta, and sizeof(bool) is different between PowerPC and Intel */ - double float_num; - }; -}; +#define LAUNCHD_DB_PREFIX "/private/var/db/launchd.db" +#define LAUNCHD_LOG_PREFIX "/private/var/log" typedef struct _launch *launch_t; @@ -66,4 +45,4 @@ launch_data_t launch_data_unpack(void *data, size_t data_size, int *fds, size_t #pragma GCC visibility pop -#endif +#endif /* __LAUNCH_INTERNAL_H__*/ diff --git a/liblaunch/launch_priv.h b/liblaunch/launch_priv.h new file mode 100644 index 0000000..878101b --- /dev/null +++ b/liblaunch/launch_priv.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +#ifndef __LAUNCH_PRIVATE_H__ +#define __LAUNCH_PRIVATE_H__ + +#include +#include +#include +#include +#include +#include + +#pragma GCC visibility push(default) + +__BEGIN_DECLS + +#define LAUNCH_KEY_SETUSERENVIRONMENT "SetUserEnvironment" +#define LAUNCH_KEY_UNSETUSERENVIRONMENT "UnsetUserEnvironment" +#define LAUNCH_KEY_SHUTDOWN "Shutdown" +#define LAUNCH_KEY_SINGLEUSER "SingleUser" +#define LAUNCH_KEY_GETRESOURCELIMITS "GetResourceLimits" +#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" +#define LAUNCHD_TRUSTED_FD_ENV "__LAUNCHD_FD" +#define LAUNCHD_ASYNC_MSG_KEY "_AsyncMessage" +#define LAUNCH_KEY_BATCHCONTROL "BatchControl" +#define LAUNCH_KEY_BATCHQUERY "BatchQuery" + +#define LAUNCH_JOBKEY_TRANSACTIONCOUNT "TransactionCount" +#define LAUNCH_JOBKEY_QUARANTINEDATA "QuarantineData" +#define LAUNCH_JOBKEY_SANDBOXPROFILE "SandboxProfile" +#define LAUNCH_JOBKEY_SANDBOXFLAGS "SandboxFlags" +#define LAUNCH_JOBKEY_SANDBOX_NAMED "Named" +#define LAUNCH_JOBKEY_JETSAMPROPERTIES "JetsamProperties" +#define LAUNCH_JOBKEY_JETSAMPRIORITY "JetsamPriority" +#define LAUNCH_JOBKEY_JETSAMMEMORYLIMIT "JetsamMemoryLimit" +#define LAUNCH_JOBKEY_SECURITYSESSIONUUID "SecuritySessionUUID" +#define LAUNCH_JOBKEY_DISABLEASLR "DisableASLR" +#define LAUNCH_JOBKEY_XPCDOMAIN "XPCDomain" +#define LAUNCH_JOBKEY_POSIXSPAWNTYPE "POSIXSpawnType" + +#define LAUNCH_KEY_JETSAMLABEL "JetsamLabel" +#define LAUNCH_KEY_JETSAMFRONTMOST "JetsamFrontmost" +#define LAUNCH_KEY_JETSAMACTIVE "JetsamActive" +#define LAUNCH_KEY_JETSAMPRIORITY LAUNCH_JOBKEY_JETSAMPRIORITY +#define LAUNCH_KEY_JETSAMMEMORYLIMIT LAUNCH_JOBKEY_JETSAMMEMORYLIMIT + +#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_EMBEDDEDMAINTHREADPRIORITY "EmbeddedMainThreadPriority" + +#define LAUNCH_JOBKEY_ENTERKERNELDEBUGGERBEFOREKILL "EnterKernelDebuggerBeforeKill" +#define LAUNCH_JOBKEY_PERJOBMACHSERVICES "PerJobMachServices" +#define LAUNCH_JOBKEY_SERVICEIPC "ServiceIPC" +#define LAUNCH_JOBKEY_BINARYORDERPREFERENCE "BinaryOrderPreference" +#define LAUNCH_JOBKEY_MACHEXCEPTIONHANDLER "MachExceptionHandler" +#define LAUNCH_JOBKEY_MULTIPLEINSTANCES "MultipleInstances" +#define LAUNCH_JOBKEY_EVENTMONITOR "EventMonitor" +#define LAUNCH_JOBKEY_SHUTDOWNMONITOR "ShutdownMonitor" +#define LAUNCH_JOBKEY_BEGINTRANSACTIONATSHUTDOWN "BeginTransactionAtShutdown" +#define LAUNCH_JOBKEY_XPCDOMAINBOOTSTRAPPER "XPCDomainBootstrapper" + +#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_ENV_INSTANCEID "LaunchInstanceID" + +/* For LoginWindow. + * + * After this call, the task's bootstrap port is set to the per session launchd. + * + * This returns 1 on success (it used to return otherwise), and -1 on failure. + */ +#define LOAD_ONLY_SAFEMODE_LAUNCHAGENTS (1 << 0) +#define LAUNCH_GLOBAL_ON_DEMAND (1 << 1) +pid_t +create_and_switch_to_per_session_launchd(const char *, int flags, ...); + +/* Also for LoginWindow. + * + * This is will load jobs at the LoginWindow prompt. + */ +void +load_launchd_jobs_at_loginwindow_prompt(int flags, ...); + +/* For CoreProcesses */ +#define SPAWN_VIA_LAUNCHD_STOPPED 0x0001 +#define SPAWN_VIA_LAUNCHD_TALAPP 0x0002 +#define SPAWN_VIA_LAUNCHD_WIDGET 0x0004 +#define SPAWN_VIA_LAUNCHD_DISABLE_ASLR 0x0008 + +struct spawn_via_launchd_attr { + uint64_t spawn_flags; + const char *spawn_path; + const char *spawn_chdir; + const char * const * spawn_env; + const mode_t *spawn_umask; + mach_port_t *spawn_observer_port; + const cpu_type_t *spawn_binpref; + size_t spawn_binpref_cnt; + void * spawn_quarantine; + const char *spawn_seatbelt_profile; + const uint64_t *spawn_seatbelt_flags; +}; + +#define spawn_via_launchd(a, b, c) _spawn_via_launchd(a, b, c, 3) +pid_t +_spawn_via_launchd(const char *label, const char * const *argv, + const struct spawn_via_launchd_attr *spawn_attrs, int struct_version); + +int +launch_wait(mach_port_t port); + +/* The mpm_*() APIs no longer do anything. */ +kern_return_t +mpm_wait(mach_port_t ajob, int *wstatus); + +kern_return_t +mpm_uncork_fork(mach_port_t ajob); + +launch_data_t +launch_socket_service_check_in(void); + +__END_DECLS + +#pragma GCC visibility pop + + +#endif /* __LAUNCH_PRIVATE_H__ */ diff --git a/launchd/src/launchd.ops b/liblaunch/launchd.ops similarity index 100% rename from launchd/src/launchd.ops rename to liblaunch/launchd.ops diff --git a/launchd/src/libbootstrap.c b/liblaunch/libbootstrap.c similarity index 99% rename from launchd/src/libbootstrap.c rename to liblaunch/libbootstrap.c index ceed582..c9f85ad 100644 --- a/launchd/src/libbootstrap.c +++ b/liblaunch/libbootstrap.c @@ -34,7 +34,7 @@ #include #include -#include "protocol_vproc.h" +#include "job.h" mach_port_t bootstrap_port = MACH_PORT_NULL; @@ -204,29 +204,29 @@ bootstrap_look_up3(mach_port_t bp, const name_t service_name, mach_port_t *sp, p if ((kr = vproc_mig_look_up2(bp, (char *)service_name, sp, &au_tok, target_pid, (unsigned char*)instance_id, flags)) != VPROC_ERR_TRY_PER_USER) { goto out; } - + if ((kr = vproc_mig_lookup_per_user_context(bp, 0, &puc)) != 0) { goto out; } - + kr = vproc_mig_look_up2(puc, (char *)service_name, sp, &au_tok, target_pid, (unsigned char*)instance_id, flags); mach_port_deallocate(mach_task_self(), puc); out: if ((kr == 0) && privileged_server_lookup) { uid_t server_euid; - + /* * The audit token magic is dependent on the per-user launchd * forwarding MIG requests to the root launchd when it cannot * find the answer locally. */ - + /* This API should be in Libsystem, but is not */ //audit_token_to_au32(au_tok, NULL, &server_euid, NULL, NULL, NULL, NULL, NULL, NULL); - + server_euid = au_tok.val[1]; - + if (server_euid) { mach_port_deallocate(mach_task_self(), *sp); *sp = MACH_PORT_NULL; diff --git a/launchd/src/liblaunch.c b/liblaunch/liblaunch.c similarity index 96% rename from launchd/src/liblaunch.c rename to liblaunch/liblaunch.c index 076f93a..0afdf0a 100644 --- a/launchd/src/liblaunch.c +++ b/liblaunch/liblaunch.c @@ -22,7 +22,7 @@ #include "launch.h" #include "launch_priv.h" #include "launch_internal.h" -#include "launchd_ktrace.h" +#include "ktrace.h" #include #include @@ -60,6 +60,31 @@ (__DARWIN_ALIGN32(sizeof(struct cmsghdr)) + (l)) #endif +struct _launch_data { + uint64_t type; + union { + struct { + union { + launch_data_t *_array; + char *string; + void *opaque; + int64_t __junk; + }; + union { + uint64_t _array_cnt; + uint64_t string_len; + uint64_t opaque_size; + }; + }; + int64_t fd; + uint64_t mp; + uint64_t err; + int64_t number; + uint64_t boolean; /* We'd use 'bool' but this struct needs to be used under Rosetta, and sizeof(bool) is different between PowerPC and Intel */ + double float_num; + }; +}; + #include "bootstrap.h" #include "vproc.h" #include "vproc_priv.h" @@ -115,6 +140,29 @@ extern void __OSBogusByteSwap__(void); _X; \ }) +union _launch_double_u { + uint64_t iv; + double dv; +}; + +#define host2wire_f(x) ({ \ + typeof(x) _F, _f = (x); \ + union _launch_double_u s; \ + s.dv = _f; \ + s.iv = host2wire(s.iv); \ + _F = s.dv; \ + _F; \ +}) + +#define big2wire_f(x) ({ \ + typeof(x) _F, _f = (x); \ + union _launch_double_u s; \ + s.dv = _f; \ + s.iv = big2wire(s.iv); \ + _F = s.dv; \ + _F; \ +}) + struct launch_msg_header { uint64_t magic; @@ -152,7 +200,7 @@ 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; -bool do_apple_internal_logging = false; +bool launchd_apple_internal = false; static struct _launch_client { pthread_mutex_t mtx; @@ -168,14 +216,14 @@ launch_client_init(void) char *_launchd_fd = getenv(LAUNCHD_TRUSTED_FD_ENV); 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) { @@ -186,10 +234,10 @@ launch_client_init(void) } unsetenv(LAUNCHD_TRUSTED_FD_ENV); } - + memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; - + /* The rules are as follows. * - All users (including root) talk to their per-user launchd's by default. * - If we have been invoked under sudo, talk to the system launchd. @@ -206,18 +254,18 @@ launch_client_init(void) } else { /* Talk to our per-user launchd. */ size_t min_len; - + min_len = sizeof(sun.sun_path) < sizeof(spath) ? sizeof(sun.sun_path) : sizeof(spath); - + strncpy(sun.sun_path, spath, min_len); } } } - + 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); #endif @@ -234,15 +282,15 @@ launch_client_init(void) goto out_bad; } } - + if (!(_lc->l = launchd_fdopen(lfd, cifd))) { goto out_bad; } - + if (!(_lc->async_resp = launch_data_alloc(LAUNCH_DATA_ARRAY))) { goto out_bad; } - + return; out_bad: if (_lc->l) @@ -566,7 +614,7 @@ launch_data_get_opaque_size(launch_data_t d) int launchd_getfd(launch_t l) { - return ( l->which == LAUNCHD_USE_CHECKIN_FD ) ? l->cifd : l->fd; + return (l->which == LAUNCHD_USE_CHECKIN_FD) ? l->cifd : l->fd; } launch_t @@ -656,7 +704,7 @@ launch_data_pack(launch_data_t d, void *where, size_t len, int *fd_where, size_t o_in_w->number = host2wire(d->number); break; case LAUNCH_DATA_REAL: - o_in_w->float_num = host2wire(d->float_num); + o_in_w->float_num = host2wire_f(d->float_num); break; case LAUNCH_DATA_BOOL: o_in_w->boolean = host2wire(d->boolean); @@ -674,16 +722,16 @@ launch_data_pack(launch_data_t d, void *where, size_t len, int *fd_where, size_t case LAUNCH_DATA_STRING: o_in_w->string_len = host2wire(d->string_len); node_data_len += ROUND_TO_64BIT_WORD_SIZE(d->string_len + 1); - + if (node_data_len > len) { return 0; } memcpy(where, d->string, d->string_len + 1); - + /* Zero padded data. */ pad_len = ROUND_TO_64BIT_WORD_SIZE(d->string_len + 1) - (d->string_len + 1); bzero(where + d->string_len + 1, pad_len); - + break; case LAUNCH_DATA_OPAQUE: o_in_w->opaque_size = host2wire(d->opaque_size); @@ -692,11 +740,11 @@ launch_data_pack(launch_data_t d, void *where, size_t len, int *fd_where, size_t return 0; } memcpy(where, d->opaque, d->opaque_size); - + /* Zero padded data. */ pad_len = ROUND_TO_64BIT_WORD_SIZE(d->opaque_size) - d->opaque_size; bzero(where + d->opaque_size, pad_len); - + break; case LAUNCH_DATA_DICTIONARY: case LAUNCH_DATA_ARRAY: @@ -781,7 +829,7 @@ launch_data_unpack(void *data, size_t data_size, int *fds, size_t fd_cnt, size_t r->number = big2wire(r->number); break; case LAUNCH_DATA_REAL: - r->float_num = big2wire(r->float_num); + r->float_num = big2wire_f(r->float_num); break; case LAUNCH_DATA_BOOL: r->boolean = big2wire(r->boolean); @@ -935,7 +983,7 @@ void launch_msg_getmsgs(launch_data_t m, void *context) { launch_data_t async_resp, *sync_resp = context; - + 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)); } else { @@ -988,11 +1036,11 @@ uuid_data_is_null(launch_data_t d) if (launch_data_get_type(d) == LAUNCH_DATA_OPAQUE && launch_data_get_opaque_size(d) == sizeof(uuid_t)) { uuid_t existing_uuid; memcpy(existing_uuid, launch_data_get_opaque(d), sizeof(uuid_t)); - + /* A NULL UUID tells us to keep the session inherited from the parent. */ result = (bool)uuid_is_null(existing_uuid); } - + return result; } @@ -1019,14 +1067,14 @@ launch_msg_internal(launch_data_t d) } else { _lc->l->which = LAUNCHD_USE_OTHER_FD; } - + fd2use = launchd_getfd(_lc->l); - + if (fd2use == -1) { errno = EPERM; return NULL; } - + #if !TARGET_OS_EMBEDDED uuid_t uuid; launch_data_t uuid_d = NULL; @@ -1075,7 +1123,7 @@ launch_msg_internal(launch_data_t d) goto out; } while (launchd_msg_send(_lc->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); @@ -1092,7 +1140,7 @@ launch_msg_internal(launch_data_t d) FD_ZERO(&rfds); FD_SET(fd2use, &rfds); - + select(fd2use + 1, &rfds, NULL, NULL, NULL); } } @@ -1103,19 +1151,19 @@ out: if (!uuid_is_null(uuid) && resp && jobs_that_need_sessions > 0) { mach_port_t session_port = _audit_session_self(); launch_data_type_t resp_type = launch_data_get_type(resp); - + bool set_session = false; if (resp_type == LAUNCH_DATA_ERRNO) { - set_session = ( launch_data_get_errno(resp) == ENEEDAUTH ); + set_session = (launch_data_get_errno(resp) == ENEEDAUTH); } else if (resp_type == LAUNCH_DATA_ARRAY) { set_session = true; } - + kern_return_t kr = KERN_FAILURE; if (set_session) { kr = vproc_mig_set_security_session(bootstrap_port, uuid, session_port); } - + if (kr == KERN_SUCCESS) { if (resp_type == LAUNCH_DATA_ERRNO) { launch_data_set_errno(resp, 0); @@ -1123,7 +1171,7 @@ out: size_t i = 0; for (i = 0; i < launch_data_array_get_count(resp); i++) { launch_data_t ri = launch_data_array_get_index(resp, i); - + int recvd_err = 0; if (launch_data_get_type(ri) == LAUNCH_DATA_ERRNO && (recvd_err = launch_data_get_errno(ri))) { launch_data_set_errno(ri, recvd_err == ENEEDAUTH ? 0 : recvd_err); @@ -1401,5 +1449,3 @@ create_and_switch_to_per_session_launchd(const char *login __attribute__((unused return 1; } - - diff --git a/launchd/src/libvproc.c b/liblaunch/libvproc.c similarity index 64% rename from launchd/src/libvproc.c rename to liblaunch/libvproc.c index 536d05f..a9151de 100644 --- a/launchd/src/libvproc.c +++ b/liblaunch/libvproc.c @@ -23,6 +23,8 @@ #include "vproc_priv.h" #include "vproc_internal.h" +#include +#include #include #include #include @@ -37,6 +39,8 @@ #include #include #include +#include +#include #if HAVE_QUARANTINE #include @@ -45,47 +49,40 @@ #include "launch.h" #include "launch_priv.h" #include "launch_internal.h" -#include "launchd_ktrace.h" +#include "ktrace.h" -#include "protocol_vproc.h" +#include "job.h" -#include "launchd_helper.h" -#include "launchd_helperServer.h" +#include "helper.h" +#include "helperServer.h" #include "reboot2.h" -#define likely(x) __builtin_expect((bool)(x), true) -#define unlikely(x) __builtin_expect((bool)(x), false) +#define likely(x) __builtin_expect((bool)(x), true) +#define unlikely(x) __builtin_expect((bool)(x), false) -static mach_port_t get_root_bootstrap_port(void); +#define _vproc_set_crash_log_message(x) -static int64_t cached_pid = -1; -static struct vproc_shmem_s *vproc_shmem; -static pthread_once_t shmem_inited = PTHREAD_ONCE_INIT; -static uint64_t s_cached_transactions_enabled = 0; +void _vproc_transactions_enable_internal(void *arg); +void _vproc_transaction_begin_internal(void *arg __unused); +void _vproc_transaction_end_internal(void *arg __unused); -static _vproc_transaction_callout vproc_gone2zero; -static _vproc_transaction_callout vproc_gonenonzero; - -static vproc_helper_recv_ping_t vprocmgr_helper_callout; +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; mach_port_t j_port; }; -/* These functions are a total nightmare to get to through headers. - * See rdar://problem/8223092. - */ -typedef __darwin_mach_port_t fileport_t; -#define FILEPORT_NULL ((fileport_t)0) -extern int fileport_makeport(int, fileport_t *); -extern int fileport_makefd(fileport_t); - -vproc_t vprocmgr_lookup_vproc(const char *label) +vproc_t +vprocmgr_lookup_vproc(const char *label) { struct vproc_s *vp = NULL; - + mach_port_t mp = MACH_PORT_NULL; kern_return_t kr = vproc_mig_port_for_label(bootstrap_port, (char *)label, &mp); if (kr == BOOTSTRAP_SUCCESS) { @@ -97,27 +94,27 @@ vproc_t vprocmgr_lookup_vproc(const char *label) } (void)mach_port_deallocate(mach_task_self(), mp); } - + return vp; } -vproc_t vproc_retain(vproc_t vp) +vproc_t +vproc_retain(vproc_t vp) { int32_t orig = OSAtomicAdd32(1, &vp->refcount) - 1; if (orig <= 0) { - /* We've gone from 0 to 1, which means that this object was due to be freed. */ _vproc_set_crash_log_message("Under-retain / over-release of vproc_t."); abort(); } - + return vp; } -void vproc_release(vproc_t vp) +void +vproc_release(vproc_t vp) { int32_t newval = OSAtomicAdd32(-1, &vp->refcount); if (newval < 0) { - /* We're in negative numbers, which is bad. */ _vproc_set_crash_log_message("Over-release of vproc_t."); abort(); } else if (newval == 0) { @@ -126,132 +123,104 @@ void vproc_release(vproc_t vp) } } +#pragma mark Transactions static void -vproc_shmem_init(void) +_vproc_transaction_init_once(void *arg __unused) { - vm_address_t vm_addr = 0; - mach_port_t shmem_port; - kern_return_t kr; - - kr = vproc_mig_setup_shmem(bootstrap_port, &shmem_port); + 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; + } + _vproc_transaction_queue = dispatch_queue_create("com.apple.idle-exit-queue", NULL); +} - //assert(kr == 0); - if (kr) { - /* rdar://problem/6416724 - * If we fail to set up a shared memory page, just allocate a local chunk - * of memory. This way, processes can still introspect their own transaction - * counts if they're being run under a debugger. Moral of the story: Debug - * from the environment you intend to run in. - */ - void *_vm_addr = calloc(1, sizeof(struct vproc_shmem_s)); - if (!_vm_addr) { - return; - } +void +_vproc_transactions_enable_internal(void *arg __unused) +{ + (void)osx_assumes_zero(proc_track_dirty(getpid(), PROC_DIRTY_TRACK)); + _vproc_transaction_enabled = 1; - vm_addr = (vm_address_t)_vm_addr; - } else { - kr = vm_map(mach_task_self(), &vm_addr, getpagesize(), 0, true, shmem_port, 0, false, - VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE); - - //assert(kr == 0); - if (kr) return; - - kr = mach_port_deallocate(mach_task_self(), shmem_port); - - //assert(kr == 0); + if (_vproc_transaction_cnt > 0) { + (void)osx_assumes_zero(proc_set_dirty(getpid(), true)); } +} - vproc_shmem = (struct vproc_shmem_s *)vm_addr; +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); } -static void -vproc_client_init(void) +void +_vproc_transaction_begin_internal(void *ctx __unused) { - char *val = getenv(LAUNCHD_DO_APPLE_INTERNAL_LOGGING); - if (val) { - if (strncmp(val, "true", sizeof("true") - 1) == 0) { - do_apple_internal_logging = true; - } + int64_t new = ++_vproc_transaction_cnt; + if (new == 1 && _vproc_transaction_enabled) { + (void)osx_assumes_zero(proc_set_dirty(getpid(), true)); } - - vproc_shmem_init(); +} + +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); } vproc_transaction_t -vproc_transaction_begin(vproc_t vp __attribute__((unused))) +vproc_transaction_begin(vproc_t vp __unused) { - vproc_transaction_t vpt = (vproc_transaction_t)vproc_shmem_init; /* we need a "random" variable that is testable */ _vproc_transaction_begin(); - return vpt; + /* 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;; } void -_vproc_transaction_begin(void) +_vproc_transaction_end_internal(void *arg __unused) { - if (unlikely(vproc_shmem == NULL)) { - int po_r = pthread_once(&shmem_inited, vproc_client_init); - if (po_r != 0 || vproc_shmem == NULL) { - return; - } + 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) { + _vproc_set_crash_log_message("Underflow of transaction count."); } +} - /* We need to deal with the potential race condition of trying to open a - * transaction after launchd has marked the process for death. Consider if - * one thread closes the last transaction after marking the process for - * death. Then we call _exit(2). But exiting isn't instantaneous. So if some - * other threads come in very soon after and try to open transactions, we - * have to cut them off so that they can't begin their work. Because if they - * can manage to get to the point of, say, parking themselves in an - * uninterruptible wait, then the process won't exit. - * - * We loop here so that, if someone's calling vproc_transaction_end() at the - * same time, we can pick up the descent to -1 if launchd has marked us for - * death. - */ - typeof(vproc_shmem->vp_shmem_transaction_cnt) old = 0; - do { - old = vproc_shmem->vp_shmem_transaction_cnt; - - if (unlikely(old < 0)) { - /* No transactions should be opened after this point, so make sure - * this thread can't proceed. We don't crash here because it could - * be a legitimate race, as described above. - */ - if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) { - _exit(0); - } else { - _vproc_set_crash_log_message("Unbalanced: vproc_transaction_begin()"); - } - abort(); - } else if (old == 0 && vproc_gonenonzero) { - vproc_gonenonzero(); - } - } while (!__sync_bool_compare_and_swap(&vproc_shmem->vp_shmem_transaction_cnt, old, old + 1)); - - runtime_ktrace(RTKT_VPROC_TRANSACTION_INCREMENT, old + 1, 0, 0); +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); +} + +void +vproc_transaction_end(vproc_t vp __unused, vproc_transaction_t vpt __unused) +{ + _vproc_transaction_end(); } size_t _vproc_transaction_count(void) { - return likely(vproc_shmem) ? vproc_shmem->vp_shmem_transaction_cnt : INT32_MAX; + return _vproc_transaction_cnt; } size_t _vproc_standby_count(void) { -#ifdef VPROC_STANDBY_IMPLEMENTED - return likely(vproc_shmem) ? vproc_shmem->vp_shmem_standby_cnt : INT32_MAX; -#else return 0; -#endif -} +} size_t _vproc_standby_timeout(void) { - return likely(vproc_shmem) ? vproc_shmem->vp_shmem_standby_timeout : 0; + return 0; } bool @@ -259,180 +228,102 @@ _vproc_pid_is_managed(pid_t p) { boolean_t result = false; vproc_mig_pid_is_managed(bootstrap_port, p, &result); - + return result; } kern_return_t _vproc_transaction_count_for_pid(pid_t p, int32_t *count, bool *condemned) { - boolean_t _condemned = false; - kern_return_t kr = vproc_mig_transaction_count_for_pid(bootstrap_port, p, count, &_condemned); - if (kr == KERN_SUCCESS && condemned) { - *condemned = _condemned ? true : false; - } - - return kr; -} + /* Activity Monitor relies on us returning this error code when the process + * is not opted into Instant Off. + */ + kern_return_t error = BOOTSTRAP_NO_MEMORY; -void -_vproc_transaction_try_exit(int status) -{ -#if !TARGET_OS_EMBEDDED - if (unlikely(vproc_shmem == NULL)) { - return; + if (condemned) { + *condemned = false; } - if (__sync_bool_compare_and_swap(&vproc_shmem->vp_shmem_transaction_cnt, 0, -1)) { - vproc_shmem->vp_shmem_flags |= VPROC_SHMEM_EXITING; - _exit(status); + if (count) { + uint32_t flags; + int ret = proc_get_dirty(p, &flags); + if (ret == 0) { + if (flags & PROC_DIRTY_TRACKED) { + *count = (flags & PROC_DIRTY_IS_DIRTY) ? 1 : 0; + error = BOOTSTRAP_SUCCESS; + } else { + error = BOOTSTRAP_NO_MEMORY; + } + } else if (ret == ENOTSUP) { + error = BOOTSTRAP_NO_MEMORY; + } else if (ret == ESRCH) { + error = BOOTSTRAP_UNKNOWN_SERVICE; + } else if (ret == EPERM) { + error = BOOTSTRAP_NOT_PRIVILEGED; + } else { + error = ret; + } } -#else -#endif + return error; } - void -vproc_transaction_end(vproc_t vp __attribute__((unused)), vproc_transaction_t vpt) -{ - if (unlikely(vpt != (vproc_transaction_t)vproc_shmem_init)) { - _vproc_set_crash_log_message("Bogus transaction handle passed to vproc_transaction_end() "); - abort(); - } - - _vproc_transaction_end(); -} - -void -_vproc_transaction_end(void) +_vproc_transaction_try_exit(int status) { - typeof(vproc_shmem->vp_shmem_transaction_cnt) newval; - - if (unlikely(vproc_shmem == NULL)) { - return; - } - - newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_transaction_cnt, 1); - - runtime_ktrace(RTKT_VPROC_TRANSACTION_DECREMENT, newval, 0, 0); - if (unlikely(newval < 0)) { - if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) { - _exit(0); - } else { - _vproc_set_crash_log_message("Unbalanced: vproc_transaction_end()"); - } - abort(); - } else if (newval == 0 && vproc_gone2zero) { - vproc_gone2zero(); +#if !TARGET_OS_EMBEDDED + if (_vproc_transaction_cnt == 0) { + _exit(status); } -} - -vproc_standby_t -vproc_standby_begin(vproc_t vp __attribute__((unused))) -{ -#ifdef VPROC_STANDBY_IMPLEMENTED - vproc_standby_t vpsb = (vproc_standby_t)vproc_shmem_init; /* we need a "random" variable that is testable */ - - _vproc_standby_begin(); - - return vpsb; #else - return NULL; + _exit(status); #endif } void _vproc_standby_begin(void) { -#ifdef VPROC_STANDBY_IMPLEMENTED - typeof(vproc_shmem->vp_shmem_standby_cnt) newval; - if (unlikely(vproc_shmem == NULL)) { - int po_r = pthread_once(&shmem_inited, vproc_client_init); - if (po_r != 0 || vproc_shmem == NULL) { - return; - } - } - - newval = __sync_add_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1); - - if (unlikely(newval < 1)) { - _vproc_set_crash_log_message("Unbalanced: vproc_standby_begin()"); - abort(); - } -#else - return; -#endif } -void -vproc_standby_end(vproc_t vp __attribute__((unused)), vproc_standby_t vpt __attribute__((unused))) +vproc_standby_t +vproc_standby_begin(vproc_t vp __unused) { -#ifdef VPROC_STANDBY_IMPLEMENTED - if (unlikely(vpt != (vproc_standby_t)vproc_shmem_init)) { - _vproc_set_crash_log_message("Bogus standby handle passed to vproc_standby_end() "); - abort(); - } - - _vproc_standby_end(); -#else - return; -#endif + return (vproc_standby_t)vproc_standby_begin; } void _vproc_standby_end(void) { -#ifdef VPROC_STANDBY_IMPLEMENTED - typeof(vproc_shmem->vp_shmem_standby_cnt) newval; - - if (unlikely(vproc_shmem == NULL)) { - _vproc_set_crash_log_message("Process called vproc_standby_end() when not enrolled in transaction model."); - abort(); - } - - newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1); - if (unlikely(newval < 0)) { - _vproc_set_crash_log_message("Unbalanced: vproc_standby_end()"); - abort(); - } -#else - return; -#endif } +/* TODO: obsoleted - remove post-build submission */ + int32_t * _vproc_transaction_ptr(void) { - return NULL; + static int32_t dummy = 1; + return &dummy; } void -_vproc_transaction_set_callouts(_vproc_transaction_callout gone2zero, _vproc_transaction_callout gonenonzero) +_vproc_transaction_set_callouts(_vproc_transaction_callout gone2zero __unused, _vproc_transaction_callout gonenonzero __unused) { - if (unlikely(vproc_shmem == NULL)) { - int po_r = pthread_once(&shmem_inited, vproc_client_init); - if (po_r != 0 || vproc_shmem == NULL) { - return; - } - } +} - static bool once = false; - if (once) { - _vproc_set_crash_log_message("This SPI may only be called once. It is only meant for libxpc."); - abort(); - } +/* */ - once = true; +void +vproc_standby_end(vproc_t vp __unused, vproc_standby_t vpt __unused) +{ - vproc_gone2zero = gone2zero; - vproc_gonenonzero = gonenonzero; } +#pragma mark Miscellaneous SPI kern_return_t -_vproc_grab_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, launch_data_t *outval, - mach_port_array_t *ports, mach_msg_type_number_t *portCnt) +_vproc_grab_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, + launch_data_t *outval, mach_port_array_t *ports, + mach_msg_type_number_t *portCnt) { mach_msg_type_number_t outdata_cnt; vm_offset_t outdata = 0; @@ -458,54 +349,6 @@ out: return kr; } -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; - } - - 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); - - 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 -} - -vproc_err_t -_vprocmgr_init(const char *session_type) -{ - if (vproc_mig_init_session(bootstrap_port, (char *)session_type, _audit_session_self()) == 0) { - return NULL; - } - - return (vproc_err_t)_vprocmgr_init; -} - vproc_err_t _vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, uint64_t flags) { @@ -532,12 +375,14 @@ _vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, uint6 return (vproc_err_t)_vprocmgr_move_subset_to_user; } - mach_port_t puc = 0, rootbs = get_root_bootstrap_port(); - + mach_port_t puc = 0; + mach_port_t rootbs = MACH_PORT_NULL; + (void)bootstrap_get_root(bootstrap_port, &rootbs); + if (vproc_mig_lookup_per_user_context(rootbs, target_user, &puc) != 0) { return (vproc_err_t)_vprocmgr_move_subset_to_user; } - + if (is_bkgd) { task_set_bootstrap_port(mach_task_self(), puc); mach_port_deallocate(mach_task_self(), bootstrap_port); @@ -546,8 +391,6 @@ _vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, uint6 kr = vproc_mig_move_subset(puc, bootstrap_port, (char *)session_type, _audit_session_self(), flags); mach_port_deallocate(mach_task_self(), puc); } - - cached_pid = -1; if (kr) { return (vproc_err_t)_vprocmgr_move_subset_to_user; @@ -568,11 +411,11 @@ _vprocmgr_switch_to_session(const char *target_session, vproc_flags_t flags __at _vproc_log(LOG_NOTICE, "_vprocmgr_switch_to_session(): kr = 0x%x", kr); return (vproc_err_t)_vprocmgr_switch_to_session; } - + task_set_bootstrap_port(mach_task_self(), new_bsport); mach_port_deallocate(mach_task_self(), bootstrap_port); bootstrap_port = new_bsport; - + return !issetugid() ? _vproc_post_fork_ping() : NULL; } @@ -582,6 +425,54 @@ _vprocmgr_detach_from_console(vproc_flags_t flags __attribute__((unused))) return _vprocmgr_switch_to_session(VPROCMGR_SESSION_BACKGROUND, 0); } +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; + } + + 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); + + 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 +} + +vproc_err_t +_vprocmgr_init(const char *session_type) +{ + if (vproc_mig_init_session(bootstrap_port, (char *)session_type, _audit_session_self()) == 0) { + return NULL; + } + + return (vproc_err_t)_vprocmgr_init; +} + pid_t _spawn_via_launchd(const char *label, const char *const *argv, const struct spawn_via_launchd_attr *spawn_attrs, int struct_version) { @@ -687,9 +578,9 @@ _spawn_via_launchd(const char *label, const char *const *argv, const struct spaw if (!eqoff) { goto out; } - + *eqoff = '\0'; - + launch_data_dict_insert(tmp_dict, launch_data_new_string(eqoff + 1), tmpstr); } @@ -882,56 +773,11 @@ _vprocmgr_log_drain(vproc_t vp __attribute__((unused)), pthread_mutex_t *mutex, vproc_err_t vproc_swap_integer(vproc_t vp, vproc_gsk_t key, int64_t *inval, int64_t *outval) { - static int64_t cached_is_managed = -1; - int64_t dummyval = 0; - - switch (key) { - case VPROC_GSK_MGR_PID: - if (cached_pid != -1 && outval) { - *outval = cached_pid; - return NULL; - } - break; - case VPROC_GSK_IS_MANAGED: - if (cached_is_managed != -1 && outval) { - *outval = cached_is_managed; - return NULL; - } - break; - case VPROC_GSK_TRANSACTIONS_ENABLED: - /* Shared memory region is required for transactions. */ - if (unlikely(vproc_shmem == NULL)) { - int po_r = pthread_once(&shmem_inited, vproc_client_init); - if (po_r != 0 || vproc_shmem == NULL) { - if (outval) { - *outval = -1; - } - return (vproc_err_t)vproc_swap_integer; - } - } - - if (s_cached_transactions_enabled && outval) { - *outval = s_cached_transactions_enabled; - return NULL; - } - break; - default: - break; - } - kern_return_t kr = KERN_FAILURE; + int64_t dummyval = 0; mach_port_t mp = vp ? vp->j_port : bootstrap_port; if ((kr = vproc_mig_swap_integer(mp, inval ? key : 0, outval ? key : 0, inval ? *inval : 0, outval ? outval : &dummyval)) == 0) { switch (key) { - case VPROC_GSK_MGR_PID: - cached_pid = outval ? *outval : dummyval; - break; - case VPROC_GSK_IS_MANAGED: - cached_is_managed = outval ? *outval : dummyval; - break; - case VPROC_GSK_TRANSACTIONS_ENABLED: - s_cached_transactions_enabled = 1; - break; case VPROC_GSK_PERUSER_SUSPEND: if (dummyval) { /* Wait for the per-user launchd to exit before returning. */ @@ -954,31 +800,6 @@ vproc_swap_integer(vproc_t vp, vproc_gsk_t key, int64_t *inval, int64_t *outval) return (vproc_err_t)vproc_swap_integer; } -mach_port_t -get_root_bootstrap_port(void) -{ - mach_port_t parent_port = 0; - mach_port_t previous_port = 0; - - do { - if (previous_port) { - if (previous_port != bootstrap_port) { - mach_port_deallocate(mach_task_self(), previous_port); - } - previous_port = parent_port; - } else { - previous_port = bootstrap_port; - } - - if (bootstrap_parent(previous_port, &parent_port) != 0) { - return MACH_PORT_NULL; - } - - } while (parent_port != previous_port); - - return parent_port; -} - vproc_err_t vproc_swap_complex(vproc_t vp, vproc_gsk_t key, launch_data_t inval, launch_data_t *outval) { @@ -1034,7 +855,7 @@ vproc_swap_string(vproc_t vp, vproc_gsk_t key, const char *instr, char **outstr) { launch_data_t instr_data = instr ? launch_data_new_string(instr) : NULL; launch_data_t outstr_data = NULL; - + vproc_err_t verr = vproc_swap_complex(vp, key, instr_data, &outstr_data); if (!verr && outstr) { if (launch_data_get_type(outstr_data) == LAUNCH_DATA_STRING) { @@ -1047,14 +868,17 @@ vproc_swap_string(vproc_t vp, vproc_gsk_t key, const char *instr, char **outstr) if (instr_data) { launch_data_free(instr_data); } - + return verr; } void * reboot2(uint64_t flags) { - if (vproc_mig_reboot2(get_root_bootstrap_port(), flags) == 0) { + mach_port_t rootbs = MACH_PORT_NULL; + (void)bootstrap_get_root(bootstrap_port, &rootbs); + if (vproc_mig_reboot2(rootbs, flags) == 0) { + (void)mach_port_deallocate(mach_task_self(), rootbs); return NULL; } @@ -1062,7 +886,9 @@ reboot2(uint64_t flags) } vproc_err_t -_vproc_kickstart_by_label(const char *label, pid_t *out_pid, mach_port_t *out_port_name __attribute__((unused)), mach_port_t *out_obsrvr_port __attribute__((unused)), vproc_flags_t flags) +_vproc_kickstart_by_label(const char *label, pid_t *out_pid, + mach_port_t *out_port_name __unused, mach_port_t *out_obsrvr_port __unused, + vproc_flags_t flags) { /* Ignore the two port parameters. This SPI isn't long for this world, and * all the current clients just leak them anyway. @@ -1118,40 +944,6 @@ _vproc_log_error(int pri, const char *msg, ...) va_end(ap); } -bool -vprocmgr_helper_check_in(const char *name, mach_port_t rp, launch_data_t *events, uint64_t *tokens) -{ - vm_offset_t events_packed = 0; - mach_msg_type_number_t sz = 0; - size_t data_off = 0; - - kern_return_t kr = vproc_mig_event_source_check_in(bootstrap_port, (char *)name, rp, &events_packed, &sz, tokens); - if (kr == 0) { - launch_data_t _events = launch_data_unpack((void *)events_packed, sz, NULL, 0, &data_off, 0); - *events = launch_data_copy(_events); - if (!*events) { - kr = 1; - } - - mig_deallocate(events_packed, sz); - } - - return (kr == 0); -} - -bool -vprocmgr_helper_event_set_state(const char *sysname, uint64_t token, bool state) -{ - kern_return_t kr = vproc_mig_event_set_state(bootstrap_port, (char *)sysname, token, state); - return (kr == 0); -} - -void -vprocmgr_helper_register(vproc_helper_recv_ping_t callout) -{ - vprocmgr_helper_callout = callout; -} - /* The type naming convention is as follows: * For requests... * union __RequestUnion___subsystem @@ -1165,23 +957,16 @@ union maxmsgsz { size_t vprocmgr_helper_maxmsgsz = sizeof(union maxmsgsz); -kern_return_t -helper_recv_ping(mach_port_t p, audit_token_t autok) -{ - return vprocmgr_helper_callout(p, autok); -} - -boolean_t -vprocmgr_helper_server_routine_for_dispatch(mach_msg_header_t *message, mach_msg_header_t *reply) -{ - return launchd_helper_server(message, reply); -} - kern_return_t helper_recv_wait(mach_port_t p, int status) { - /* Total hack. */ - return (errno = mach_port_set_context(mach_task_self(), p, (mach_vm_address_t)status)); +#if __LAUNCH_MACH_PORT_CONTEXT_T_DEFINED__ + mach_port_context_t ctx = status; +#else + mach_vm_address_t ctx = status; +#endif + + return (errno = mach_port_set_context(mach_task_self(), p, ctx)); } int @@ -1190,7 +975,11 @@ launch_wait(mach_port_t port) int status = -1; errno = mach_msg_server_once(launchd_helper_server, vprocmgr_helper_maxmsgsz, port, 0); if (errno == MACH_MSG_SUCCESS) { +#if __LAUNCH_MACH_PORT_CONTEXT_T_DEFINED__ + mach_port_context_t ctx = 0; +#else mach_vm_address_t ctx = 0; +#endif if ((errno = mach_port_get_context(mach_task_self(), port, &ctx)) == KERN_SUCCESS) { status = ctx; } @@ -1198,3 +987,49 @@ launch_wait(mach_port_t port) return status; } + +launch_data_t +launch_socket_service_check_in(void) +{ + launch_data_t reply = NULL; + + size_t big_enough = 10 * 1024; + void *buff = malloc(big_enough); + if (buff) { + launch_data_t req = launch_data_new_string(LAUNCH_KEY_CHECKIN); + if (req) { + size_t sz = launch_data_pack(req, buff, big_enough, NULL, NULL); + if (sz) { + vm_address_t sreply = 0; + mach_msg_size_t sreplyCnt = 0; + mach_port_array_t fdps = NULL; + mach_msg_size_t fdpsCnt = 0; + kern_return_t kr = vproc_mig_legacy_ipc_request(bootstrap_port, (vm_address_t)buff, sz, NULL, 0, &sreply, &sreplyCnt, &fdps, &fdpsCnt, _audit_session_self()); + if (kr == BOOTSTRAP_SUCCESS) { + int fds[128]; + + size_t i = 0; + size_t nfds = fdpsCnt / sizeof(fdps[0]); + for (i = 0; i < nfds; i++) { + fds[i] = fileport_makefd(fdps[i]); + (void)mach_port_deallocate(mach_task_self(), fdps[i]); + } + + size_t dataoff = 0; + size_t fdoff = 0; + reply = launch_data_unpack((void *)sreply, sreplyCnt, fds, nfds, &dataoff, &fdoff); + reply = launch_data_copy(reply); + + mig_deallocate(sreply, sreplyCnt); + mig_deallocate((vm_address_t)fdps, fdpsCnt); + } + } + + launch_data_free(req); + } + + free(buff); + } + + return reply; +} diff --git a/launchd/src/reboot2.h b/liblaunch/reboot2.h similarity index 86% rename from launchd/src/reboot2.h rename to liblaunch/reboot2.h index 2bb5cab..d5037e1 100644 --- a/launchd/src/reboot2.h +++ b/liblaunch/reboot2.h @@ -1,5 +1,3 @@ -#ifndef _REBOOT2_H_ -#define _REBOOT2_H_ /* * Copyright (c) 2007 Apple Inc. All rights reserved. * @@ -20,6 +18,9 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ +#ifndef __REBOOT2_H__ +#define __REBOOT2_H__ + #include #include #include @@ -30,8 +31,9 @@ __BEGIN_DECLS /* Returns NULL on success. Not NULL on failure */ -__attribute__((visibility("default"))) void *reboot2(uint64_t flags); +__attribute__((visibility("default"))) +void *reboot2(uint64_t flags); __END_DECLS -#endif +#endif /* __REBOOT2_H__ */ diff --git a/launchd/src/vproc.h b/liblaunch/vproc.h similarity index 98% rename from launchd/src/vproc.h rename to liblaunch/vproc.h index ac5a67f..065e71c 100644 --- a/launchd/src/vproc.h +++ b/liblaunch/vproc.h @@ -1,5 +1,3 @@ -#ifndef _VPROC_H_ -#define _VPROC_H_ /* * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. * @@ -20,6 +18,9 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ +#ifndef __VPROC_H__ +#define __VPROC_H__ + #include #include #include @@ -167,4 +168,4 @@ vproc_standby_end(vproc_t virtual_proc, vproc_standby_t handle) __OSX_AVAILABLE_ __END_DECLS -#endif +#endif /* __VPROC_H__ */ diff --git a/launchd/src/vproc_internal.h b/liblaunch/vproc_internal.h similarity index 74% rename from launchd/src/vproc_internal.h rename to liblaunch/vproc_internal.h index b295aaf..a47553e 100644 --- a/launchd/src/vproc_internal.h +++ b/liblaunch/vproc_internal.h @@ -1,5 +1,3 @@ -#ifndef _VPROC_INTERNAL_H_ -#define _VPROC_INTERNAL_H_ /* * Copyright (c) 2006-2007 Apple Inc. All rights reserved. * @@ -20,6 +18,9 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ +#ifndef __VPROC_INTERNAL_H__ +#define __VPROC_INTERNAL_H__ + #include #include #include @@ -34,23 +35,13 @@ typedef char * _internal_string_t; typedef char * logmsg_t; typedef pid_t * pid_array_t; typedef mach_port_t vproc_mig_t; -typedef uint64_t event_token_array_t[1024]; - -#define VPROC_SHMEM_EXITING 0x1 -struct vproc_shmem_s { - int32_t vp_shmem_transaction_cnt; - int32_t vp_shmem_standby_cnt; - uint32_t vp_shmem_standby_timeout; - int32_t vp_shmem_flags; -}; - -#if defined(protocol_vproc_MSG_COUNT) || defined (xpc_domain_MSG_COUNT) || defined (xpc_events_MSG_COUNT) +#if defined(job_MSG_COUNT) || defined (xpc_domain_MSG_COUNT) /* HACK */ -#include "launchd_core_logic.h" +#include "core.h" #endif -#define VPROC_ERR_TRY_PER_USER 1099 +#define VPROC_ERR_TRY_PER_USER 1099 #pragma GCC visibility push(default) @@ -58,17 +49,23 @@ vproc_err_t _vprocmgr_init(const char *session_type); vproc_err_t _vproc_post_fork_ping(void); #if !TARGET_OS_EMBEDDED - #define _audit_session_self(v) (mach_port_t)syscall(SYS_audit_session_self) - #define _audit_session_join(s) (au_asid_t)syscall(SYS_audit_session_join, session) +#define _audit_session_self(v) (mach_port_t)syscall(SYS_audit_session_self) +#define _audit_session_join(s) (au_asid_t)syscall(SYS_audit_session_join, session) #else - #define _audit_session_self(v) MACH_PORT_NULL - #define _audit_session_join(s) 0 +#define _audit_session_self(v) MACH_PORT_NULL +#define _audit_session_join(s) 0 #endif -#define SPAWN_HAS_PATH 0x0001 -#define SPAWN_HAS_WDIR 0x0002 -#define SPAWN_HAS_UMASK 0x0004 -#define SPAWN_WANTS_WAIT4DEBUGGER 0x0008 +#if __arm__ +#define __LAUNCH_MACH_PORT_CONTEXT_T_DEFINED__ 1 +#else +#define __LAUNCH_MACH_PORT_CONTEXT_T_DEFINED__ 0 +#endif + +#define SPAWN_HAS_PATH 0x0001 +#define SPAWN_HAS_WDIR 0x0002 +#define SPAWN_HAS_UMASK 0x0004 +#define SPAWN_WANTS_WAIT4DEBUGGER 0x0008 kern_return_t _vproc_grab_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, launch_data_t *outval, @@ -111,7 +108,6 @@ struct logmsg_s { vproc_err_t _vprocmgr_log_forward(mach_port_t mp, void *data, size_t len); - kern_return_t bootstrap_info(mach_port_t bp, name_array_t *service_names, @@ -124,4 +120,4 @@ bootstrap_info(mach_port_t bp, #pragma GCC visibility pop -#endif +#endif /* __VPROC_INTERNAL_H__ */ diff --git a/launchd/src/vproc_priv.h b/liblaunch/vproc_priv.h similarity index 52% rename from launchd/src/vproc_priv.h rename to liblaunch/vproc_priv.h index 8893628..954b481 100644 --- a/launchd/src/vproc_priv.h +++ b/liblaunch/vproc_priv.h @@ -1,5 +1,3 @@ -#ifndef _VPROC_PRIVATE_H_ -#define _VPROC_PRIVATE_H_ /* * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. * @@ -20,6 +18,9 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ +#ifndef __VPROC_PRIVATE_H__ +#define __VPROC_PRIVATE_H__ + #include #include #include @@ -32,22 +33,32 @@ #include #ifndef VPROC_HAS_TRANSACTIONS - #define VPROC_HAS_TRANSACTIONS +#define VPROC_HAS_TRANSACTIONS 1 #endif __BEGIN_DECLS +#define VPROCMGR_SESSION_LOGINWINDOW "LoginWindow" +#define VPROCMGR_SESSION_BACKGROUND "Background" +#define VPROCMGR_SESSION_AQUA "Aqua" +#define VPROCMGR_SESSION_STANDARDIO "StandardIO" +#define VPROCMGR_SESSION_SYSTEM "System" + +#define XPC_DOMAIN_TYPE_SYSTEM "XPCSystem" +#define XPC_DOMAIN_TYPE_PERUSER "XPCPerUser" +#define XPC_DOMAIN_TYPE_PERSESSION "XPCPerSession" +#define XPC_DOMAIN_TYPE_PERAPPLICATION "XPCPerApplication" + #pragma GCC visibility push(default) /* DO NOT use this. This is a hack for 'launchctl' */ -#define VPROC_MAGIC_UNLOAD_SIGNAL 0x4141504C -/* DO NOT use this. This is a hack for 'loginwindow' */ -#define VPROC_MAGIC_TRYKILL_SIGNAL 0x6161706C +#define VPROC_MAGIC_UNLOAD_SIGNAL 0x4141504C typedef void (*_vproc_transaction_callout)(void); typedef enum { - VPROC_GSK_LAST_EXIT_STATUS = 1, + VPROC_GSK_ZERO, + VPROC_GSK_LAST_EXIT_STATUS, VPROC_GSK_GLOBAL_ON_DEMAND, VPROC_GSK_MGR_UID, VPROC_GSK_MGR_PID, @@ -75,54 +86,87 @@ typedef enum { VPROC_GSK_EMBEDDEDROOTEQUIVALENT, } vproc_gsk_t; -typedef kern_return_t (*vproc_helper_recv_ping_t)(mach_port_t, audit_token_t); - -/* Give to dispatch_mig_server(). */ -boolean_t vprocmgr_helper_server_routine_for_dispatch(mach_msg_header_t *message, mach_msg_header_t *reply); - -/* Give to dispatch_mig_server(). */ -extern size_t vprocmgr_helper_maxmsgsz; - typedef unsigned int vproc_flags_t; /* For _vproc_kickstart_by_label() -- instructs launchd to kickstart the job to stall before exec(2). */ #define VPROCFLAG_STALL_JOB_EXEC 1 << 1 -vproc_t vprocmgr_lookup_vproc(const char *label); -vproc_t vproc_retain(vproc_t vp); -void vproc_release(vproc_t vp); - -vproc_err_t vproc_swap_integer(vproc_t vp, vproc_gsk_t key, int64_t *inval, int64_t *outval); -vproc_err_t vproc_swap_complex(vproc_t vp, vproc_gsk_t key, launch_data_t inval, launch_data_t *outval); -vproc_err_t vproc_swap_string(vproc_t vp, vproc_gsk_t key, const char *instr, char **outstr); +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0) +vproc_t +vprocmgr_lookup_vproc(const char *label); -vproc_err_t _vproc_get_last_exit_status(int *wstatus); -vproc_err_t _vproc_set_global_on_demand(bool val); +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0) +vproc_t +vproc_retain(vproc_t vp); -typedef void (*_vprocmgr_log_drain_callback_t)(struct timeval *when, pid_t from_pid, pid_t about_pid, uid_t sender_uid, gid_t sender_gid, int priority, const char *from_name, const char *about_name, const char *session_name, const char *msg); - -vproc_err_t _vprocmgr_log_drain(vproc_t vp, pthread_mutex_t *optional_mutex_around_callback, _vprocmgr_log_drain_callback_t func); +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0) +void +vproc_release(vproc_t vp); + +__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_3_0) +vproc_err_t +vproc_swap_integer(vproc_t vp, vproc_gsk_t key, + int64_t *inval, int64_t *outval); + +__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_3_0) +vproc_err_t +vproc_swap_complex(vproc_t vp, vproc_gsk_t key, + launch_data_t inval, launch_data_t *outval); + +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_4_0) +vproc_err_t +vproc_swap_string(vproc_t vp, vproc_gsk_t key, + const char *instr, char **outstr); + +__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA) +vproc_err_t +_vproc_get_last_exit_status(int *wstatus); + +__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_3_0) +vproc_err_t +_vproc_set_global_on_demand(bool val); + +__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_3_0) +vproc_err_t +_vproc_send_signal_by_label(const char *label, int sig); + +__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_3_0) +vproc_err_t +_vproc_kickstart_by_label(const char *label, pid_t *out_pid, + mach_port_t *out_port_name, mach_port_t *out_obsrvr_port, + vproc_flags_t flags); + +/* _vprocmgr_log_drain() is specific to syslogd. It is not for general use. */ +typedef void (*_vprocmgr_log_drain_callback_t)(struct timeval *when, pid_t + from_pid, pid_t about_pid, uid_t sender_uid, gid_t sender_gid, int priority, + const char *from_name, const char *about_name, const char *session_name, + const char *msg); + +vproc_err_t +_vprocmgr_log_drain(vproc_t vp, pthread_mutex_t *optional_mutex_around_callback, + _vprocmgr_log_drain_callback_t func); + +__attribute__((format(printf, 2, 3))) +void +_vproc_log(int pri, const char *msg, ...); -vproc_err_t _vproc_send_signal_by_label(const char *label, int sig); -vproc_err_t _vproc_kickstart_by_label(const char *label, pid_t *out_pid, mach_port_t *out_port_name, mach_port_t *out_obsrvr_port, vproc_flags_t flags); +__attribute__((format(printf, 2, 3))) +void +_vproc_log_error(int pri, const char *msg, ...); -void _vproc_log(int pri, const char *msg, ...) __attribute__((format(printf, 2, 3))); -void _vproc_log_error(int pri, const char *msg, ...) __attribute__((format(printf, 2, 3))); -void _vproc_logv(int pri, int err, const char *msg, va_list ap) __attribute__((format(printf, 3, 0))); +__attribute__((format(printf, 3, 0))) +void +_vproc_logv(int pri, int err, const char *msg, va_list ap); -#define VPROCMGR_SESSION_LOGINWINDOW "LoginWindow" -#define VPROCMGR_SESSION_BACKGROUND "Background" -#define VPROCMGR_SESSION_AQUA "Aqua" -#define VPROCMGR_SESSION_STANDARDIO "StandardIO" -#define VPROCMGR_SESSION_SYSTEM "System" +/* One day, we'll be able to get rid of this... */ +vproc_err_t +_vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, + uint64_t flags); -#define XPC_DOMAIN_TYPE_SYSTEM "XPCSystem" -#define XPC_DOMAIN_TYPE_PERUSER "XPCPerUser" -#define XPC_DOMAIN_TYPE_PERSESSION "XPCPerSession" -#define XPC_DOMAIN_TYPE_PERAPPLICATION "XPCPerApplication" +vproc_err_t +_vprocmgr_switch_to_session(const char *target_session, vproc_flags_t flags); -vproc_err_t _vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, uint64_t flags); -vproc_err_t _vprocmgr_switch_to_session(const char *target_session, vproc_flags_t flags); -vproc_err_t _vprocmgr_detach_from_console(vproc_flags_t flags); +vproc_err_t +_vprocmgr_detach_from_console(vproc_flags_t flags); __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) void @@ -170,17 +214,11 @@ _vproc_transaction_ptr(void); void _vproc_transaction_set_callouts(_vproc_transaction_callout gone2zero, _vproc_transaction_callout gonenonzero); -bool -vprocmgr_helper_check_in(const char *sysname, mach_port_t rp, launch_data_t *events, uint64_t *tokens); - -bool -vprocmgr_helper_event_set_state(const char *sysname, uint64_t token, bool state); - void -vprocmgr_helper_register(vproc_helper_recv_ping_t callout); +_vproc_transactions_enable(void); #pragma GCC visibility pop __END_DECLS -#endif +#endif /* __VPROC_PRIVATE_H__ */ diff --git a/launchd/src/launchctl.1 b/man/launchctl.1 similarity index 100% rename from launchd/src/launchctl.1 rename to man/launchctl.1 diff --git a/launchd/src/launchd.8 b/man/launchd.8 similarity index 100% rename from launchd/src/launchd.8 rename to man/launchd.8 diff --git a/launchd/src/launchd.conf.5 b/man/launchd.conf.5 similarity index 100% rename from launchd/src/launchd.conf.5 rename to man/launchd.conf.5 diff --git a/launchd/src/launchd.plist.5 b/man/launchd.plist.5 similarity index 100% rename from launchd/src/launchd.plist.5 rename to man/launchd.plist.5 diff --git a/launchd/src/launchproxy.8 b/man/launchproxy.8 similarity index 100% rename from launchd/src/launchproxy.8 rename to man/launchproxy.8 diff --git a/launchd/src/rc.8 b/man/rc.8 similarity index 100% rename from launchd/src/rc.8 rename to man/rc.8 diff --git a/launchd/src/wait4path.1 b/man/wait4path.1 similarity index 100% rename from launchd/src/wait4path.1 rename to man/wait4path.1 diff --git a/launchd/src/rc.common b/rc/rc.common similarity index 100% rename from launchd/src/rc.common rename to rc/rc.common diff --git a/launchd/src/rc.netboot b/rc/rc.netboot similarity index 100% rename from launchd/src/rc.netboot rename to rc/rc.netboot diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..841f524 --- /dev/null +++ b/src/config.h @@ -0,0 +1,20 @@ +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#include + +#if __has_include() +#define HAVE_QUARANTINE 1 +#else +#define HAVE_QUARANTINE 0 +#endif + +#if __has_include() +#define HAVE_SANDBOX 1 +#else +#define HAVE_SANDBOX 0 +#endif + +#define HAVE_LIBAUDITD !TARGET_OS_EMBEDDED + +#endif /* __CONFIG_H__ */ diff --git a/launchd/src/launchd_core_logic.c b/src/core.c similarity index 70% rename from launchd/src/launchd_core_logic.c rename to src/core.c index a91441b..68b6ade 100644 --- a/launchd/src/launchd_core_logic.c +++ b/src/core.c @@ -16,12 +16,10 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ -static const char *const __rcs_file_version__ = "$Revision: 26200 $"; - #include "config.h" -#include "launchd_core_logic.h" -#include "launch_internal.h" -#include "launchd_helper.h" +#include "core.h" +#include "internal.h" +#include "helper.h" #include #include @@ -55,6 +53,7 @@ static const char *const __rcs_file_version__ = "$Revision: 26200 $"; #include #include #include +#include #include #include #include @@ -78,11 +77,16 @@ static const char *const __rcs_file_version__ = "$Revision: 26200 $"; #include #include #include +#include #include +#include #include #include +#include +#include #include +#include #include #include #include @@ -93,25 +97,8 @@ static const char *const __rcs_file_version__ = "$Revision: 26200 $"; #if HAVE_QUARANTINE #include #endif -#if TARGET_OS_EMBEDDED -#include -#else +#if !TARGET_OS_EMBEDDED extern int gL1CacheEnabled; -/* To make my life easier. */ -typedef struct jetsam_priority_entry { - pid_t pid; - uint32_t priority; - uint32_t flags; - int32_t hiwat_pages; - int32_t hiwat_reserved1; - int32_t hiwat_reserved2; - int32_t hiwat_reserved3; -} jetsam_priority_entry_t; - -enum { - kJetsamFlagsFrontmost = (1 << 0), - kJetsamFlagsKilled = (1 << 1) -}; #endif #include "launch.h" @@ -125,47 +112,32 @@ enum { #include "reboot2.h" #include "launchd.h" -#include "launchd_runtime.h" -#include "launchd_unix_ipc.h" -#include "protocol_vproc.h" -#include "protocol_vprocServer.h" -#include "protocol_job_reply.h" -#include "protocol_job_forward.h" +#include "runtime.h" +#include "ipc.h" +#include "job.h" +#include "jobServer.h" +#include "job_reply.h" +#include "job_forward.h" #include "mach_excServer.h" -#if !TARGET_OS_EMBEDDED -#include "domainServer.h" -#include "init.h" -#endif /* !TARGET_OS_EMBEDDED */ -#include "eventsServer.h" - -#ifndef POSIX_SPAWN_OSX_TALAPP_START -#define POSIX_SPAWN_OSX_TALAPP_START 0x0400 -#endif - -#ifndef POSIX_SPAWN_OSX_WIDGET_START -#define POSIX_SPAWN_OSX_WIDGET_START 0x0800 -#endif -#ifndef POSIX_SPAWN_IOS_APP_START -#define POSIX_SPAWN_IOS_APP_START 0x1000 -#endif +#define POSIX_SPAWN_IOS_INTERACTIVE 0 /* 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. */ -#define LAUNCHD_MIN_JOB_RUN_TIME 10 -#define LAUNCHD_DEFAULT_EXIT_TIMEOUT 20 -#define LAUNCHD_SIGKILL_TIMER 2 -#define LAUNCHD_LOG_FAILED_EXEC_FREQ 10 +#define LAUNCHD_MIN_JOB_RUN_TIME 10 +#define LAUNCHD_DEFAULT_EXIT_TIMEOUT 20 +#define LAUNCHD_SIGKILL_TIMER 4 +#define LAUNCHD_LOG_FAILED_EXEC_FREQ 10 #define SHUTDOWN_LOG_DIR "/var/log/shutdown" -#define TAKE_SUBSET_NAME "TakeSubsetName" -#define TAKE_SUBSET_PID "TakeSubsetPID" -#define TAKE_SUBSET_PERPID "TakeSubsetPerPID" +#define TAKE_SUBSET_NAME "TakeSubsetName" +#define TAKE_SUBSET_PID "TakeSubsetPID" +#define TAKE_SUBSET_PERPID "TakeSubsetPerPID" -#define IS_POWER_OF_TWO(v) (!(v & (v - 1)) && v) +#define IS_POWER_OF_TWO(v) (!(v & (v - 1)) && v) extern char **environ; @@ -183,45 +155,44 @@ struct machservice { LIST_ENTRY(machservice) name_hash_sle; LIST_ENTRY(machservice) port_hash_sle; struct machservice *alias; - job_t job; - unsigned int gen_num; - mach_port_name_t port; + job_t job; + unsigned int gen_num; + mach_port_name_t port; unsigned int - isActive :1, - reset :1, - recv :1, - hide :1, - kUNCServer :1, - per_user_hack :1, - debug_on_close :1, - per_pid :1, - delete_on_destruction :1, - drain_one_on_crash :1, - drain_all_on_crash :1, - event_update_port :1, /* The job which owns this port is the event monitor. */ - upfront :1, /* This service was declared in the plist. */ - event_channel :1, /* The job is to receive events on this channel. */ - /* 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. + isActive:1, + reset:1, + recv:1, + hide:1, + kUNCServer:1, + per_user_hack:1, + debug_on_close:1, + per_pid:1, + delete_on_destruction:1, + drain_one_on_crash:1, + drain_all_on_crash:1, + upfront:1, + event_channel: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; - const char name[0]; + special_port_num:18; + const char name[0]; }; -static SLIST_HEAD(, machservice) special_ports; /* hack, this should be per jobmgr_t */ +// HACK: This should be per jobmgr_t +static SLIST_HEAD(, machservice) special_ports; #define PORT_HASH_SIZE 32 -#define HASH_PORT(x) (IS_POWER_OF_TWO(PORT_HASH_SIZE) ? (MACH_PORT_INDEX(x) & (PORT_HASH_SIZE - 1)) : (MACH_PORT_INDEX(x) % PORT_HASH_SIZE)) +#define HASH_PORT(x) (IS_POWER_OF_TWO(PORT_HASH_SIZE) ? (MACH_PORT_INDEX(x) & (PORT_HASH_SIZE - 1)) : (MACH_PORT_INDEX(x) % PORT_HASH_SIZE)) static LIST_HEAD(, machservice) port_hash[PORT_HASH_SIZE]; static void machservice_setup(launch_data_t obj, const char *key, void *context); static void machservice_setup_options(launch_data_t obj, const char *key, void *context); static void machservice_resetport(job_t j, struct machservice *ms); +static void machservice_stamp_port(job_t j, struct machservice *ms); static struct machservice *machservice_new(job_t j, const char *name, mach_port_t *serviceport, bool pid_local); -#ifndef __LAUNCH_DISABLE_XPC_SUPPORT__ static struct machservice *machservice_new_alias(job_t aj, struct machservice *orig); -#endif /* __LAUNCH_DISABLE_XPC_SUPPORT__ */ static void machservice_ignore(job_t j, struct machservice *ms); static void machservice_watch(job_t j, struct machservice *ms); static void machservice_delete(job_t j, struct machservice *, bool port_died); @@ -233,19 +204,18 @@ static bool machservice_active(struct machservice *); static const char *machservice_name(struct machservice *); static bootstrap_status_t machservice_status(struct machservice *); void machservice_drain_port(struct machservice *); -static struct machservice *xpc_events_find_channel(job_t j, event_name_t stream, mach_port_t *p); struct socketgroup { SLIST_ENTRY(socketgroup) sle; int *fds; - unsigned int junkfds:1, fd_cnt:31; + unsigned int fd_cnt; union { const char name[0]; char name_init[0]; }; }; -static bool socketgroup_new(job_t j, const char *name, int *fds, size_t fd_cnt, bool junkfds); +static bool socketgroup_new(job_t j, const char *name, int *fds, size_t fd_cnt); static void socketgroup_delete(job_t j, struct socketgroup *sg); static void socketgroup_watch(job_t j, struct socketgroup *sg); static void socketgroup_ignore(job_t j, struct socketgroup *sg); @@ -273,7 +243,6 @@ static void calendarinterval_sanity_check(void); struct envitem { SLIST_ENTRY(envitem) sle; - bool one_shot; char *value; union { const char key[0]; @@ -281,10 +250,9 @@ struct envitem { }; }; -static bool envitem_new(job_t j, const char *k, const char *v, bool global, bool one_shot); +static bool envitem_new(job_t j, const char *k, const char *v, bool global); static void envitem_delete(job_t j, struct envitem *ei, bool global); static void envitem_setup(launch_data_t obj, const char *key, void *context); -static void envitem_setup_one_shot(launch_data_t obj, const char *key, void *context); struct limititem { SLIST_ENTRY(limititem) sle; @@ -308,23 +276,16 @@ typedef enum { FAILED_EXIT, CRASHED, DID_NOT_CRASH, - PATH_EXISTS, - PATH_MISSING, OTHER_JOB_ENABLED, OTHER_JOB_DISABLED, OTHER_JOB_ACTIVE, OTHER_JOB_INACTIVE, - PATH_CHANGES, - DIR_NOT_EMPTY, - // FILESYSTEMTYPE_IS_MOUNTED, /* for nfsiod, but maybe others */ } semaphore_reason_t; struct semaphoreitem { SLIST_ENTRY(semaphoreitem) sle; semaphore_reason_t why; - bool watching_parent; - int fd; - + union { const char what[0]; char what_init[0]; @@ -341,22 +302,20 @@ static bool semaphoreitem_new(job_t j, semaphore_reason_t why, const char *what) static void semaphoreitem_delete(job_t j, struct semaphoreitem *si); static void semaphoreitem_setup(launch_data_t obj, const char *key, void *context); static void semaphoreitem_setup_dict_iter(launch_data_t obj, const char *key, void *context); -static void semaphoreitem_callback(job_t j, struct kevent *kev); -static void semaphoreitem_watch(job_t j, struct semaphoreitem *si); -static void semaphoreitem_ignore(job_t j, struct semaphoreitem *si); static void semaphoreitem_runtime_mod_ref(struct semaphoreitem *si, bool add); struct externalevent { LIST_ENTRY(externalevent) sys_le; LIST_ENTRY(externalevent) job_le; struct eventsystem *sys; - + uint64_t id; job_t job; bool state; bool wanted_state; - launch_data_t event; - + bool internal; + xpc_object_t event; + char name[0]; }; @@ -365,7 +324,7 @@ struct externalevent_iter_ctx { struct eventsystem *sys; }; -static bool externalevent_new(job_t j, struct eventsystem *sys, char *evname, launch_data_t event); +static bool externalevent_new(job_t j, struct eventsystem *sys, const char *evname, xpc_object_t event); 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); @@ -374,18 +333,17 @@ struct eventsystem { LIST_ENTRY(eventsystem) global_le; LIST_HEAD(, externalevent) events; uint64_t curid; - bool has_updates; char name[0]; }; static struct eventsystem *eventsystem_new(const char *name); -static void eventsystem_delete(struct eventsystem *sys); +static void eventsystem_delete(struct eventsystem *sys) __attribute__((unused)); 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); -#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)) +#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)) #define MACHSERVICE_HASH_SIZE 37 @@ -396,12 +354,11 @@ struct jobmgr_s { SLIST_ENTRY(jobmgr_s) sle; SLIST_HEAD(, jobmgr_s) submgrs; LIST_HEAD(, job_s) jobs; - LIST_HEAD(, job_s) jetsam_jobs; - - /* 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 its own label hash that is separate from the "global" one - * stored in the root job manager. + + /* 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 + * its own label hash that is separate from the "global" one stored in the + * root job manager. */ LIST_HEAD(, job_s) label_hash[LABEL_HASH_SIZE]; LIST_HEAD(, job_s) active_jobs[ACTIVE_JOB_HASH_SIZE]; @@ -414,17 +371,16 @@ struct jobmgr_s { time_t shutdown_time; unsigned int global_on_demand_cnt; unsigned int normal_active_cnt; - unsigned int jetsam_jobs_cnt; unsigned int - shutting_down :1, - session_initialized :1, - killed_stray_jobs :1, - monitor_shutdown :1, - shutdown_jobs_dirtied :1, - shutdown_jobs_cleaned :1, - xpc_singleton :1; + shutting_down:1, + session_initialized:1, + killed_stray_jobs:1, + monitor_shutdown:1, + shutdown_jobs_dirtied:1, + shutdown_jobs_cleaned:1, + xpc_singleton:1; uint32_t properties; - /* XPC-specific properties. */ + // XPC-specific properties. char owner[MAXCOMLEN]; char *shortdesc; mach_port_t req_bsport; @@ -444,23 +400,19 @@ struct jobmgr_s { }; }; -/* Global XPC domains. */ -#ifndef __LAUNCH_DISABLE_XPC_SUPPORT__ +// Global XPC domains. 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; -#endif /* __LAUNCH_DISABLE_XPC_SUPPORT__ */ -#define jobmgr_assumes(jm, e) \ - (unlikely(!(e)) ? jobmgr_log_bug(jm, __LINE__), false : true) +#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_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); -#ifndef __LAUNCH_DISABLE_XPC_SUPPORT__ static jobmgr_t jobmgr_new_xpc_singleton_domain(jobmgr_t jm, name_t name); static jobmgr_t jobmgr_find_xpc_per_user_domain(jobmgr_t jm, uid_t uid); static jobmgr_t jobmgr_find_xpc_per_session_domain(jobmgr_t jm, au_asid_t asid); -static job_t xpc_domain_import_service(jobmgr_t jm, launch_data_t pload); -#endif /* __LAUNCH_DISABLE_XPC_SUPPORT__ */ static job_t jobmgr_import2(jobmgr_t jm, launch_data_t pload); static jobmgr_t jobmgr_parent(jobmgr_t jm); static jobmgr_t jobmgr_do_garbage_collection(jobmgr_t jm); @@ -483,8 +435,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_error(jobmgr_t jm, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); */ -static void jobmgr_log_bug(jobmgr_t jm, unsigned int line); +static void jobmgr_log_perf_statistics(jobmgr_t jm); +// 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); #define AUTO_PICK_LEGACY_LABEL (const char *)(~0) #define AUTO_PICK_ANONYMOUS_LABEL (const char *)(~1) @@ -496,7 +449,8 @@ struct suspended_peruser { }; struct job_s { - kq_callback kqjob_callback; /* MUST be first element of this structure for benefit of launchd's run loop. */ + // MUST be first element of this structure. + kq_callback kqjob_callback; LIST_ENTRY(job_s) sle; LIST_ENTRY(job_s) subjob_sle; LIST_ENTRY(job_s) needing_session_sle; @@ -538,9 +492,8 @@ struct job_s { char *stdoutpath; char *stderrpath; char *alt_exc_handler; - struct vproc_shmem_s *shmem; - struct machservice *lastlookup; - unsigned int lastlookup_gennum; + unsigned int nruns; + uint64_t trt; #if HAVE_SANDBOX char *seatbelt_profile; uint64_t seatbelt_flags; @@ -553,13 +506,10 @@ struct job_s { int last_exit_status; int stdin_fd; int fork_fd; - int log_redirect_fd; int nice; - int stdout_err_fd; uint32_t pstype; int32_t jetsam_priority; int32_t jetsam_memlimit; - int32_t jetsam_seq; int32_t main_thread_priority; uint32_t timeout; uint32_t exit_timeout; @@ -567,91 +517,186 @@ struct job_s { uint64_t start_time; uint32_t min_run_time; uint32_t start_interval; - uint32_t peruser_suspend_count; /* The number of jobs that have disabled this per-user launchd. */ + uint32_t peruser_suspend_count; uuid_t instance_id; - uint32_t fail_cnt; -#if 0 - /* someday ... */ - enum { - J_TYPE_ANONYMOUS = 1, - J_TYPE_LANCHSERVICES, - J_TYPE_MACHINIT, - J_TYPE_INETD, - } j_type; -#endif - bool - debug :1, /* man launchd.plist --> Debug */ - ondemand :1, /* man launchd.plist --> KeepAlive == false */ - session_create :1, /* man launchd.plist --> SessionCreate */ - low_pri_io :1, /* man launchd.plist --> LowPriorityIO */ - no_init_groups :1, /* man launchd.plist --> InitGroups */ - priv_port_has_senders :1, /* a legacy mach_init concept to make bootstrap_create_server/service() work */ - importing_global_env :1, /* a hack during job importing */ - importing_hard_limits :1, /* a hack during job importing */ - setmask :1, /* man launchd.plist --> Umask */ - anonymous :1, /* a process that launchd knows about, but isn't managed by launchd */ - checkedin :1, /* a legacy mach_init concept to detect sick jobs */ - legacy_mach_job :1, /* a job created via bootstrap_create_server() */ - legacy_LS_job :1, /* a job created via spawn_via_launchd() */ - inetcompat :1, /* a legacy job that wants inetd compatible semantics */ - inetcompat_wait :1, /* a twist on inetd compatibility */ - start_pending :1, /* an event fired and the job should start, but not necessarily right away */ - globargv :1, /* man launchd.plist --> EnableGlobbing */ - wait4debugger :1, /* man launchd.plist --> WaitForDebugger */ - wait4debugger_oneshot :1, /* One-shot WaitForDebugger. */ - internal_exc_handler :1, /* MachExceptionHandler == true */ - stall_before_exec :1, /* a hack to support an option of spawn_via_launchd() */ - only_once :1, /* man launchd.plist --> LaunchOnlyOnce. Note: 5465184 Rename this to "HopefullyNeverExits" */ - currently_ignored :1, /* Make job_ignore() / job_watch() work. If these calls were balanced, then this wouldn't be necessarily. */ - forced_peers_to_demand_mode :1, /* A job that forced all other jobs to be temporarily launch-on-demand */ - setnice :1, /* man launchd.plist --> Nice */ - removal_pending :1, /* a job was asked to be unloaded/removed while running, we'll remove it after it exits */ - sent_sigkill :1, /* job_kill() was called */ - debug_before_kill :1, /* enter the kernel debugger before killing a job */ - weird_bootstrap :1, /* a hack that launchd+launchctl use during jobmgr_t creation */ - start_on_mount :1, /* man launchd.plist --> StartOnMount */ - per_user :1, /* This job is a per-user launchd managed by the PID 1 launchd */ - unload_at_mig_return :1, /* A job thoroughly confused launchd. We need to unload it ASAP */ - abandon_pg :1, /* man launchd.plist --> AbandonProcessGroup */ - ignore_pg_at_shutdown :1, /* During shutdown, do not send SIGTERM to stray processes in the process group of this job. */ - poll_for_vfs_changes :1, /* a hack to work around the fact that kqueues don't work on all filesystems */ - deny_job_creation :1, /* Don't let this job create new 'job_t' objects in launchd */ - kill_via_shmem :1, /* man launchd.plist --> EnableTransactions */ - sent_kill_via_shmem :1, /* We need to 'kill_via_shmem' once-and-only-once */ - clean_kill :1, /* The job was sent SIGKILL because it was clean. */ - kill_after_sample :1, /* The job is to be killed after sampling. */ - reap_after_trace :1, /* The job exited before sample did, so we should reap it after sample is done. */ - nosy :1, /* The job has an OtherJobEnabled KeepAlive criterion. */ - crashed :1, /* The job is the default Mach exception handler, and it crashed. */ - reaped :1, /* We've received NOTE_EXIT for the job. */ - stopped :1, /* job_stop() was called. */ - jetsam_frontmost :1, /* The job is considered "frontmost" by Jetsam. */ - needs_kickoff :1, /* The job is to be kept alive continuously, but it must be initially kicked off. */ - is_bootstrapper :1, /* The job is a bootstrapper. */ - has_console :1, /* The job owns the console. */ - embedded_special_privileges :1, /* The job runs as a non-root user on embedded but has select privileges of the root user. */ - did_exec :1, /* The job exec(2)ed successfully. */ - xpcproxy_did_exec :1, /* The job is an XPC service, and XPC proxy successfully exec(3)ed. */ - holds_ref :1, /* The (anonymous) job called vprocmgr_switch_to_session(). */ - jetsam_properties :1, /* The job has Jetsam limits in place. */ - dedicated_instance :1, /* This job was created as the result of a look up of a service provided by a per-lookup job. */ - multiple_instances :1, /* The job supports creating additional instances of itself. */ - former_subjob :1, /* The sub-job was already removed from the parent's list of sub-jobs. */ - event_monitor :1, /* The job is responsible for monitoring external events for this launchd. */ - removing :1, /* A lame hack. */ - disable_aslr :1, /* Disable ASLR when launching this job. */ - xpc_service :1, /* The job is an XPC Service. */ - shutdown_monitor :1, /* The job is the Performance team's shutdown monitor. */ - dirty_at_shutdown :1, /* We should open a transaction for the job when shutdown begins. */ - workaround9359725 :1, /* The job was sent SIGKILL but did not exit in a timely fashion, indicating a kernel bug. */ - xpc_bootstrapper :1; - mode_t mask; pid_t tracing_pid; mach_port_t asport; - /* Only set for per-user launchd's. */ + // Only set for per-user launchd's. au_asid_t asid; uuid_t expected_audit_uuid; + bool + // man launchd.plist --> Debug + debug:1, + // man launchd.plist --> KeepAlive == false + ondemand:1, + // man launchd.plist --> SessionCreate + session_create:1, + // man launchd.plist --> LowPriorityIO + low_pri_io:1, + // man launchd.plist --> InitGroups + no_init_groups:1, + /* A legacy mach_init concept to make bootstrap_create_server/service() + * work + */ + priv_port_has_senders:1, + // A hack during job importing + importing_global_env:1, + // A hack during job importing + importing_hard_limits:1, + // man launchd.plist --> Umask + setmask:1, + // A process that launchd knows about but doesn't manage. + anonymous:1, + // A legacy mach_init concept to detect sick jobs + checkedin:1, + // A job created via bootstrap_create_server() + legacy_mach_job:1, + // A job created via spawn_via_launchd() + legacy_LS_job:1, + // A legacy job that wants inetd compatible semantics + inetcompat:1, + // A twist on inetd compatibility + inetcompat_wait:1, + /* An event fired and the job should start, but not necessarily right + * away. + */ + start_pending:1, + // man launchd.plist --> EnableGlobbing + globargv:1, + // man launchd.plist --> WaitForDebugger + wait4debugger:1, + // One-shot WaitForDebugger. + wait4debugger_oneshot:1, + // MachExceptionHandler == true + internal_exc_handler:1, + // A hack to support an option of spawn_via_launchd() + stall_before_exec:1, + /* man launchd.plist --> LaunchOnlyOnce. + * + * Note: Rename this to "HopefullyNeverExits". + */ + only_once:1, + /* Make job_ignore() / job_watch() work. If these calls were balanced, + * then this wouldn't be necessarily. + */ + currently_ignored:1, + /* A job that forced all other jobs to be temporarily launch-on- + * demand + */ + forced_peers_to_demand_mode:1, + // man launchd.plist --> Nice + setnice:1, + /* A job was asked to be unloaded/removed while running, we'll remove it + * after it exits. + */ + removal_pending:1, + // job_kill() was called. + sent_sigkill:1, + // Enter the kernel debugger before killing a job. + debug_before_kill:1, + // A hack that launchd+launchctl use during jobmgr_t creation. + weird_bootstrap:1, + // man launchd.plist --> StartOnMount + start_on_mount:1, + // This job is a per-user launchd managed by the PID 1 launchd. + per_user:1, + // A job thoroughly confused launchd. We need to unload it ASAP. + unload_at_mig_return:1, + // man launchd.plist --> AbandonProcessGroup + abandon_pg:1, + /* During shutdown, do not send SIGTERM to stray processes in the + * process group of this job. + */ + ignore_pg_at_shutdown:1, + /* Don't let this job create new 'job_t' objects in launchd. Has been + * seriously overloaded for the purposes of sandboxing. + */ + deny_job_creation:1, + // man launchd.plist --> EnableTransactions + enable_transactions:1, + // The job was sent SIGKILL because it was clean. + clean_kill:1, + /* The job has a tracing PID (probably a debugger) and exited before the + * tracer did. So we must defer our reap attempt until after the tracer + * has exited. This works around our busted ptrace(3) implementation. + */ + reap_after_trace:1, + // The job has an OtherJobEnabled KeepAlive criterion. + nosy:1, + // The job exited due to a crash. + crashed:1, + // We've received NOTE_EXIT for the job and reaped it. + 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. + */ + needs_kickoff:1, + // The job is a bootstrapper. + is_bootstrapper:1, + // The job owns the console. + has_console:1, + /* The job runs as a non-root user on embedded but has select privileges + * of the root user. This is SpringBoard. + */ + embedded_god:1, + // We got NOTE_EXEC for the job. + did_exec:1, + // The job is an XPC service, and XPC proxy successfully exec(3)ed. + xpcproxy_did_exec:1, + // The (anonymous) job called vprocmgr_switch_to_session(). + holds_ref:1, + // The job has Jetsam limits in place. + jetsam_properties:1, + /* This job was created as the result of a look up of a service provided + * by a MultipleInstance job. + */ + dedicated_instance:1, + // The job supports creating additional instances of itself. + multiple_instances:1, + /* The sub-job was already removed from the parent's list of + * sub-jobs. + */ + former_subjob:1, + /* The job is responsible for monitoring external events for this + * launchd. + */ + event_monitor:1, + // The event monitor job has retrieved the initial list of events. + event_monitor_ready2signal:1, + // A lame hack. + removing:1, + // Disable ASLR when launching this job. + disable_aslr:1, + // The job is an XPC Service. + xpc_service:1, + // The job is the Performance team's shutdown monitor. + shutdown_monitor:1, + // We should open a transaction for the job when shutdown begins. + dirty_at_shutdown:1, + /* The job was sent SIGKILL but did not exit in a timely fashion, + * indicating a kernel bug. + */ + workaround9359725:1, + // The job is the XPC domain bootstrapper. + xpc_bootstrapper:1, + // The job is an app (on either iOS or OS X) and has different resource + // limitations. + app: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; + const char label[0]; }; @@ -659,8 +704,9 @@ 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; -#define job_assumes(j, e) \ - (unlikely(!(e)) ? job_log_bug(j, __LINE__), false : true) +#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_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); static void job_import_bool(job_t j, const char *key, bool value); @@ -696,22 +742,22 @@ static void job_log_stray_pg(job_t j); static void job_log_children_without_exec(job_t j); static job_t job_new_anonymous(jobmgr_t jm, pid_t anonpid) __attribute__((malloc, nonnull, warn_unused_result)); static job_t job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *argv) __attribute__((malloc, nonnull(1,2), warn_unused_result)); -#ifndef __LAUNCH_DISABLE_XPC_SUPPORT__ static job_t job_new_alias(jobmgr_t jm, job_t src); -#endif /* __LAUNCH_DISABLE_XPC_SUPPORT__ */ static job_t job_new_via_mach_init(job_t j, const char *cmd, uid_t uid, bool ond) __attribute__((malloc, nonnull, warn_unused_result)); static job_t job_new_subjob(job_t j, uuid_t identifier); static void job_kill(job_t j); static void job_uncork_fork(job_t j); -static void job_log_stdouterr(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 void job_log_bug(job_t j, unsigned int line); -static void job_log_stdouterr2(job_t j, const char *msg, ...); +static bool job_log_bug(aslmsg asl_message, void *ctx, const char *message); +static void job_log_perf_statistics(job_t j); 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); 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 const struct { const char *key; @@ -735,59 +781,60 @@ static bool cronemu_mday(struct tm *wtm, int mday, int hour, int min); static bool cronemu_hour(struct tm *wtm, int hour, int min); static bool cronemu_min(struct tm *wtm, int min); -/* These functions are a total nightmare to get to through headers. - * See rdar://problem/8223092. - */ -typedef __darwin_mach_port_t fileport_t; -#define FILEPORT_NULL ((fileport_t)0) -extern int fileport_makeport(int, fileport_t *); -extern int fileport_makefd(fileport_t); - -/* miscellaneous file local functions */ +// miscellaneous file local functions static size_t get_kern_max_proc(void); -static int dir_has_files(job_t j, const char *path); static char **mach_cmd2argv(const char *string); static size_t our_strhash(const char *s) __attribute__((pure)); -static void extract_rcsid_substr(const char *i, char *o, size_t osz); void eliminate_double_reboot(void); -/* file local globals */ +#pragma mark XPC Domain Forward Declarations +static job_t _xpc_domain_import_service(jobmgr_t jm, launch_data_t pload); +static int _xpc_domain_import_services(job_t j, launch_data_t services); + +#pragma mark XPC Event Forward Declarations +static int xpc_event_find_channel(job_t j, const char *stream, struct machservice **ms); +static int xpc_event_get_event_name(job_t j, xpc_object_t request, xpc_object_t *reply); +static int xpc_event_set_event(job_t j, xpc_object_t request, xpc_object_t *reply); +static int xpc_event_copy_event(job_t j, xpc_object_t request, xpc_object_t *reply); +static int xpc_event_channel_check_in(job_t j, xpc_object_t request, xpc_object_t *reply); +static int xpc_event_channel_look_up(job_t j, xpc_object_t request, xpc_object_t *reply); +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); + +// file local globals +static job_t _launchd_embedded_god = NULL; static size_t total_children; static size_t total_anon_children; static mach_port_t the_exception_server; static job_t workaround_5477111; static LIST_HEAD(, job_s) s_needing_sessions; static LIST_HEAD(, eventsystem) _s_event_systems; -static job_t _s_event_monitor; -static job_t _s_xpc_bootstrapper; -static job_t _s_shutdown_monitor; -static mach_port_t _s_event_update_port; -mach_port_t g_audit_session_port = MACH_PORT_NULL; -static uint32_t s_jetsam_sequence_id; +static struct eventsystem *_launchd_support_system; +static job_t _launchd_event_monitor; +static job_t _launchd_xpc_bootstrapper; +static job_t _launchd_shutdown_monitor; +mach_port_t launchd_audit_port = MACH_PORT_NULL; #if !TARGET_OS_EMBEDDED -static job_t s_embedded_privileged_job = (job_t)&root_jobmgr; -au_asid_t g_audit_session = AU_DEFAUDITSID; +au_asid_t launchd_audit_session = AU_DEFAUDITSID; #else -static job_t s_embedded_privileged_job = NULL; -pid_t g_audit_session = 0; +pid_t launchd_audit_session = 0; #endif static int s_no_hang_fd = -1; -/* process wide globals */ +// process wide globals mach_port_t inherited_bootstrap_port; jobmgr_t root_jobmgr; -bool g_shutdown_debugging = false; -bool g_verbose_boot = false; -bool g_embedded_privileged_action = false; -bool g_runtime_busy_time = false; +bool launchd_shutdown_debugging = false; +bool launchd_verbose_boot = false; +bool launchd_embedded_handofgod = false; +bool launchd_runtime_busy_time = false; void job_ignore(job_t j) { - struct semaphoreitem *si; struct socketgroup *sg; struct machservice *ms; @@ -799,11 +846,6 @@ job_ignore(job_t j) j->currently_ignored = true; - if (j->poll_for_vfs_changes) { - j->poll_for_vfs_changes = false; - (void)job_assumes(j, kevent_mod((uintptr_t)&j->semaphores, EVFILT_TIMER, EV_DELETE, 0, 0, j) != -1); - } - SLIST_FOREACH(sg, &j->sockets, sle) { socketgroup_ignore(j, sg); } @@ -811,16 +853,11 @@ job_ignore(job_t j) SLIST_FOREACH(ms, &j->machservices, sle) { machservice_ignore(j, ms); } - - SLIST_FOREACH(si, &j->semaphores, sle) { - semaphoreitem_ignore(j, si); - } } void job_watch(job_t j) { - struct semaphoreitem *si; struct socketgroup *sg; struct machservice *ms; @@ -839,76 +876,76 @@ job_watch(job_t j) SLIST_FOREACH(ms, &j->machservices, sle) { machservice_watch(j, ms); } - - SLIST_FOREACH(si, &j->semaphores, sle) { - semaphoreitem_watch(j, si); - } } void job_stop(job_t j) { - char extralog[100]; - int32_t newval = 1; + int sig; if (unlikely(!j->p || j->stopped || j->anonymous)) { return; } #if TARGET_OS_EMBEDDED - if (g_embedded_privileged_action && s_embedded_privileged_job) { - if (!job_assumes(j, s_embedded_privileged_job->username != NULL && j->username != NULL)) { + if (launchd_embedded_handofgod && _launchd_embedded_god) { + if (!_launchd_embedded_god->username || !j->username) { errno = EPERM; return; } - - if (strcmp(j->username, s_embedded_privileged_job->username) != 0) { + + if (strcmp(j->username, _launchd_embedded_god->username) != 0) { errno = EPERM; return; } - } else if (g_embedded_privileged_action) { + } else if (launchd_embedded_handofgod) { errno = EINVAL; return; } #endif - if (j->kill_via_shmem) { - if (j->shmem) { - if (!j->sent_kill_via_shmem) { - j->shmem->vp_shmem_flags |= VPROC_SHMEM_EXITING; - newval = __sync_sub_and_fetch(&j->shmem->vp_shmem_transaction_cnt, 1); - j->sent_kill_via_shmem = true; - } else { - newval = j->shmem->vp_shmem_transaction_cnt; - } - } else { - newval = -1; - } - } - j->sent_signal_time = runtime_get_opaque_time(); - if (newval < 0) { - j->clean_kill = true; - job_kill(j); - } else { - (void)job_assumes(j, runtime_kill(j->p, SIGTERM) != -1); + job_log(j, LOG_DEBUG | LOG_CONSOLE, "Stopping job..."); - if (j->exit_timeout) { - (void)job_assumes(j, kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, j->exit_timeout, j) != -1); + int error = -1; + error = proc_terminate(j->p, &sig); + if (error) { + job_log(j, LOG_ERR | LOG_CONSOLE, "Could not terminate job: %d: %s", error, strerror(error)); + job_log(j, LOG_NOTICE | LOG_CONSOLE, "Using fallback option to terminate job..."); + error = kill2(j->p, SIGTERM); + if (error) { + job_log(j, LOG_ERR, "Could not signal job: %d: %s", error, strerror(error)); } else { - job_log(j, LOG_NOTICE, "This job has an infinite exit timeout"); + sig = SIGTERM; } + } - if (j->kill_via_shmem) { - snprintf(extralog, sizeof(extralog), ": %d remaining transactions", newval + 1); - } else { - extralog[0] = '\0'; - } + if (!error) { + switch (sig) { + case SIGKILL: + j->sent_sigkill = true; + j->clean_kill = true; + error = kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, LAUNCHD_SIGKILL_TIMER, j); + (void)job_assumes_zero_p(j, error); - job_log(j, LOG_DEBUG, "Sent SIGTERM signal%s", extralog); + job_log(j, LOG_DEBUG | LOG_CONSOLE, "Sent job SIGKILL."); + break; + case SIGTERM: + if (j->exit_timeout) { + error = kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, j->exit_timeout, j); + (void)job_assumes_zero_p(j, error); + } else { + job_log(j, LOG_NOTICE, "This job has an infinite exit timeout"); + } + job_log(j, LOG_DEBUG, "Sent job SIGTERM."); + break; + default: + job_log(j, LOG_ERR | LOG_CONSOLE, "Job was sent unexpected signal: %d: %s", sig, strsignal(sig)); + break; + } } - + j->stopped = true; } @@ -963,22 +1000,8 @@ job_export(job_t j) launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_PROGRAMARGUMENTS); } - if (j->kill_via_shmem && (tmp = launch_data_new_bool(true))) { - int32_t tmp_cnt = -1; - + if (j->enable_transactions && (tmp = launch_data_new_bool(true))) { launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_ENABLETRANSACTIONS); - - if (j->shmem) { - tmp_cnt = j->shmem->vp_shmem_transaction_cnt; - } - - if (j->sent_kill_via_shmem) { - tmp_cnt++; - } - - if ((tmp = launch_data_new_integer(tmp_cnt))) { - launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_TRANSACTIONCOUNT); - } } if (j->session_create && (tmp = launch_data_new_bool(true))) { @@ -994,12 +1017,9 @@ job_export(job_t j) if (!SLIST_EMPTY(&j->sockets) && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) { struct socketgroup *sg; - int i; + unsigned int i; SLIST_FOREACH(sg, &j->sockets, sle) { - if (sg->junkfds) { - continue; - } if ((tmp2 = launch_data_alloc(LAUNCH_DATA_ARRAY))) { for (i = 0; i < sg->fd_cnt; i++) { if ((tmp3 = launch_data_new_fd(sg->fds[i]))) { @@ -1015,7 +1035,7 @@ job_export(job_t j) if (!SLIST_EMPTY(&j->machservices) && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) { struct machservice *ms; - + tmp3 = NULL; SLIST_FOREACH(ms, &j->machservices, sle) { @@ -1054,10 +1074,33 @@ jobmgr_log_active_jobs(jobmgr_t jm) jobmgr_log_active_jobs(jmi); } + int level = LOG_DEBUG; + if (pid1_magic) { + level |= LOG_CONSOLE; + } + LIST_FOREACH(ji, &jm->jobs, sle) { if ((why_active = job_active(ji))) { if (ji->p != 1) { - job_log(ji, LOG_DEBUG | LOG_CONSOLE, "%s", why_active); + job_log(ji, level, "%s", why_active); + + uint32_t flags = 0; + (void)proc_get_dirty(ji->p, &flags); + if (!(flags & PROC_DIRTY_TRACKED)) { + continue; + } + + char *dirty = "clean"; + if (flags & PROC_DIRTY_IS_DIRTY) { + dirty = "dirty"; + } + + char *idle_exit = "idle-exit unsupported"; + if (flags & PROC_DIRTY_ALLOWS_IDLE_EXIT) { + idle_exit = "idle-exit supported"; + } + + job_log(ji, level, "Killability: %s/%s", dirty, idle_exit); } } } @@ -1066,8 +1109,14 @@ jobmgr_log_active_jobs(jobmgr_t jm) static void jobmgr_still_alive_with_check(jobmgr_t jm) { - jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "Still alive with %lu/%lu (normal/anonymous) children.", total_children, total_anon_children); + int level = LOG_DEBUG; + if (pid1_magic) { + level |= LOG_CONSOLE; + } + + jobmgr_log(jm, level, "Still alive with %lu/%lu (normal/anonymous) children.", total_children, total_anon_children); jobmgr_log_active_jobs(jm); + launchd_log_push(); } jobmgr_t @@ -1083,7 +1132,7 @@ jobmgr_shutdown(jobmgr_t jm) char date[26]; (void)asctime_r(&curtime, date); - /* Trim the new line that asctime_r(3) puts there for some reason. */ + // Trim the new line that asctime_r(3) puts there for some reason. date[24] = 0; if (jm == root_jobmgr && pid1_magic) { @@ -1097,15 +1146,17 @@ jobmgr_shutdown(jobmgr_t jm) SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) { jobmgr_shutdown(jmi); } - - if (jm->parentmgr == NULL && pid1_magic) { - (void)jobmgr_assumes(jm, kevent_mod((uintptr_t)jm, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 5, jm)); - /* Spawn the shutdown monitor. */ - if (_s_shutdown_monitor && !_s_shutdown_monitor->p) { - job_log(_s_shutdown_monitor, LOG_NOTICE | LOG_CONSOLE, "Starting shutdown monitor."); - job_dispatch(_s_shutdown_monitor, true); + if (!jm->parentmgr) { + if (pid1_magic) { + // Spawn the shutdown monitor. + if (_launchd_shutdown_monitor && !_launchd_shutdown_monitor->p) { + job_log(_launchd_shutdown_monitor, LOG_NOTICE | LOG_CONSOLE, "Starting shutdown monitor."); + job_dispatch(_launchd_shutdown_monitor, true); + } } + + (void)jobmgr_assumes_zero_p(jm, kevent_mod((uintptr_t)jm, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 5, jm)); } return jobmgr_do_garbage_collection(jm); @@ -1118,49 +1169,51 @@ jobmgr_remove(jobmgr_t jm) job_t ji; jobmgr_log(jm, LOG_DEBUG, "Removing job manager."); - if (!jobmgr_assumes(jm, SLIST_EMPTY(&jm->submgrs))) { + if (!SLIST_EMPTY(&jm->submgrs)) { + size_t cnt = 0; while ((jmi = SLIST_FIRST(&jm->submgrs))) { jobmgr_remove(jmi); + cnt++; } + + (void)jobmgr_assumes_zero(jm, cnt); } while ((ji = LIST_FIRST(&jm->jobs))) { - if (!ji->anonymous && !job_assumes(ji, ji->p == 0)) { + if (!ji->anonymous && ji->p != 0) { + job_log(ji, LOG_ERR, "Job is still active at job manager teardown."); ji->p = 0; } job_remove(ji); } if (jm->req_port) { - (void)jobmgr_assumes(jm, launchd_mport_deallocate(jm->req_port) == KERN_SUCCESS); + (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(jm->req_port)); } if (jm->jm_port) { - (void)jobmgr_assumes(jm, launchd_mport_close_recv(jm->jm_port) == KERN_SUCCESS); + (void)jobmgr_assumes_zero(jm, launchd_mport_close_recv(jm->jm_port)); } if (jm->req_bsport) { - (void)jobmgr_assumes(jm, launchd_mport_deallocate(jm->req_bsport) == KERN_SUCCESS); + (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(jm->req_bsport)); } if (jm->req_excport) { - (void)jobmgr_assumes(jm, launchd_mport_deallocate(jm->req_excport) == KERN_SUCCESS); + (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(jm->req_excport)); } - if (jm->req_asport) { - (void)jobmgr_assumes(jm, launchd_mport_deallocate(jm->req_asport) == KERN_SUCCESS); + if (MACH_PORT_VALID(jm->req_asport)) { + (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(jm->req_asport)); } -#if !TARGET_OS_EMBEDDED if (jm->req_rport) { kern_return_t kr = xpc_call_wakeup(jm->req_rport, jm->error); if (!(kr == KERN_SUCCESS || kr == MACH_SEND_INVALID_DEST)) { /* If the originator went away, the reply port will be a dead name, * and we expect this to fail. */ - errno = kr; - (void)jobmgr_assumes(jm, kr == KERN_SUCCESS); + (void)jobmgr_assumes_zero(jm, kr); } } -#endif /* !TARGET_OS_EMBEDDED */ if (jm->req_ctx) { - (void)jobmgr_assumes(jm, vm_deallocate(mach_task_self(), jm->req_ctx, jm->req_ctx_sz) == KERN_SUCCESS); + (void)jobmgr_assumes_zero(jm, vm_deallocate(mach_task_self(), jm->req_ctx, jm->req_ctx_sz)); } time_t ts = runtime_get_wall_time() / USEC_PER_SEC; @@ -1188,14 +1241,14 @@ jobmgr_remove(jobmgr_t jm) launchd_log_vm_stats(); jobmgr_log_stray_children(jm, true); jobmgr_log(root_jobmgr, LOG_NOTICE | LOG_CONSOLE, "About to call: reboot(%s).", reboot_flags_to_C_names(jm->reboot_flags)); - runtime_closelog(); - (void)jobmgr_assumes(jm, reboot(jm->reboot_flags) != -1); + launchd_closelog(); + (void)jobmgr_assumes_zero_p(jm, reboot(jm->reboot_flags)); } else { jobmgr_log(jm, LOG_DEBUG, "About to exit"); - runtime_closelog(); + launchd_closelog(); exit(EXIT_SUCCESS); } - + free(jm); } @@ -1226,32 +1279,33 @@ job_remove(job_t j) } #if TARGET_OS_EMBEDDED - if (g_embedded_privileged_action && s_embedded_privileged_job) { - if (!job_assumes(j, s_embedded_privileged_job->username != NULL && j->username != NULL)) { + if (launchd_embedded_handofgod && _launchd_embedded_god) { + if (!(_launchd_embedded_god->username && j->username)) { errno = EPERM; return; } - - if (strcmp(j->username, s_embedded_privileged_job->username) != 0) { + + if (strcmp(j->username, _launchd_embedded_god->username) != 0) { errno = EPERM; return; } - } else if (g_embedded_privileged_action) { + } else if (launchd_embedded_handofgod) { errno = EINVAL; return; } #endif - - /* Do this BEFORE we check and see whether the job is still active. If we're a - * sub-job, we're being removed due to the parent job removing us. Therefore, the - * parent job will free itself after this call completes. So if we defer removing - * ourselves from the parent's list, we'll crash when we finally get around to it. + + /* Do this BEFORE we check and see whether the job is still active. If we're + * a sub-job, we're being removed due to the parent job removing us. + * Therefore, the parent job will free itself after this call completes. So + * if we defer removing ourselves from the parent's list, we'll crash when + * we finally get around to it. */ if (j->dedicated_instance && !j->former_subjob) { LIST_REMOVE(j, subjob_sle); j->former_subjob = true; } - + if (unlikely(j->p)) { if (j->anonymous) { job_reap(j); @@ -1262,11 +1316,11 @@ job_remove(job_t j) j->removal_pending = true; job_stop(j); } - + return; } } - + if (!j->removing) { j->removing = true; job_dispatch_curious_jobs(j); @@ -1274,33 +1328,20 @@ job_remove(job_t j) ipc_close_all_with_job(j); - job_log(j, LOG_INFO, "Total rusage: utime %ld.%06u stime %ld.%06u maxrss %lu ixrss %lu idrss %lu isrss %lu minflt %lu majflt %lu nswap %lu inblock %lu oublock %lu msgsnd %lu msgrcv %lu nsignals %lu nvcsw %lu nivcsw %lu", - j->ru.ru_utime.tv_sec, j->ru.ru_utime.tv_usec, - j->ru.ru_stime.tv_sec, j->ru.ru_stime.tv_usec, - j->ru.ru_maxrss, j->ru.ru_ixrss, j->ru.ru_idrss, j->ru.ru_isrss, - j->ru.ru_minflt, j->ru.ru_majflt, - j->ru.ru_nswap, j->ru.ru_inblock, j->ru.ru_oublock, - j->ru.ru_msgsnd, j->ru.ru_msgrcv, - j->ru.ru_nsignals, j->ru.ru_nvcsw, j->ru.ru_nivcsw); - if (j->forced_peers_to_demand_mode) { job_set_global_on_demand(j, false); } - if (!job_assumes(j, j->fork_fd == 0)) { - (void)job_assumes(j, runtime_close(j->fork_fd) != -1); + if (job_assumes_zero(j, j->fork_fd)) { + (void)posix_assumes_zero(runtime_close(j->fork_fd)); } if (j->stdin_fd) { - (void)job_assumes(j, runtime_close(j->stdin_fd) != -1); - } - - if (!job_assumes(j, j->log_redirect_fd == 0)) { - (void)job_assumes(j, runtime_close(j->log_redirect_fd) != -1); + (void)posix_assumes_zero(runtime_close(j->stdin_fd)); } if (j->j_port) { - (void)job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_close_recv(j->j_port)); } while ((sg = SLIST_FIRST(&j->sockets))) { @@ -1327,37 +1368,18 @@ job_remove(job_t j) while ((w4r = SLIST_FIRST(&j->removal_watchers))) { waiting4removal_delete(j, w4r); } - + struct externalevent *eei = NULL; while ((eei = LIST_FIRST(&j->events))) { - eventsystem_ping(); externalevent_delete(eei); } -#if 0 - /* Event systems exist independently of an actual monitor job. They're - * created on-demand when a job has a LaunchEvents dictionary. So we - * really don't need to get rid of them. - */ if (j->event_monitor) { - struct eventsystem *esi = NULL; - while ((esi = LIST_FIRST(&_s_event_systems))) { - eventsystem_delete(esi); - } - } -#else - if (false) { - /* Make gcc happy. */ - eventsystem_delete(NULL); + _launchd_event_monitor = NULL; } - if (j->event_monitor) { - if (_s_event_update_port != MACH_PORT_NULL) { - (void)job_assumes(j, launchd_mport_deallocate(_s_event_update_port) == KERN_SUCCESS); - _s_event_update_port = MACH_PORT_NULL; - } - _s_event_monitor = NULL; + if (j->xpc_bootstrapper) { + _launchd_xpc_bootstrapper = NULL; } -#endif if (j->prog) { free(j->prog); @@ -1404,33 +1426,28 @@ job_remove(job_t j) } if (j->start_interval) { runtime_del_weak_ref(); - (void)job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1); - } - if (j->poll_for_vfs_changes) { - (void)job_assumes(j, kevent_mod((uintptr_t)&j->semaphores, EVFILT_TIMER, EV_DELETE, 0, 0, j) != -1); + (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL)); } if (j->exit_timeout) { - /* Not a big deal if this fails. It means that the timer's already been freed. */ - kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); - } - if (j->jetsam_properties) { - LIST_REMOVE(j, jetsam_sle); - j->mgr->jetsam_jobs_cnt--; + /* If this fails, it just means the timer's already fired, so no need to + * wrap it in an assumes() macro. + */ + (void)kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); } if (j->asport != MACH_PORT_NULL) { - (void)job_assumes(j, launchd_mport_deallocate(j->asport) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_deallocate(j->asport)); } if (!uuid_is_null(j->expected_audit_uuid)) { LIST_REMOVE(j, needing_session_sle); } - if (j->embedded_special_privileges) { - s_embedded_privileged_job = NULL; + if (j->embedded_god) { + _launchd_embedded_god = NULL; } if (j->shutdown_monitor) { - _s_shutdown_monitor = NULL; + _launchd_shutdown_monitor = NULL; } - kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); + (void)kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); LIST_REMOVE(j, sle); LIST_REMOVE(j, label_hash_sle); @@ -1440,7 +1457,7 @@ job_remove(job_t j) LIST_FOREACH_SAFE(ji, &j->subjobs, subjob_sle, jit) { job_remove(ji); } - + job_log(j, LOG_DEBUG, "Removed"); j->kqjob_callback = (kq_callback)0x8badf00d; @@ -1471,7 +1488,7 @@ socketgroup_setup(launch_data_t obj, const char *key, void *context) fds[i] = launch_data_get_fd(tmp_oai); } - socketgroup_new(j, key, fds, fd_cnt, strcmp(key, LAUNCH_JOBKEY_BONJOURFDS) == 0); + socketgroup_new(j, key, fds, fd_cnt); ipc_revoke_fds(obj); } @@ -1501,30 +1518,22 @@ job_set_global_on_demand(job_t j, bool val) bool job_setup_machport(job_t j) { - mach_msg_size_t mxmsgsz; - - if (!job_assumes(j, launchd_mport_create_recv(&j->j_port) == KERN_SUCCESS)) { + if (job_assumes_zero(j, launchd_mport_create_recv(&j->j_port)) != KERN_SUCCESS) { goto out_bad; } - /* Sigh... at the moment, MIG has maxsize == sizeof(reply union) */ - mxmsgsz = (typeof(mxmsgsz)) sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem); - if (job_mig_protocol_vproc_subsystem.maxsize > mxmsgsz) { - mxmsgsz = job_mig_protocol_vproc_subsystem.maxsize; - } - - if (!job_assumes(j, runtime_add_mport(j->j_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS)) { + if (job_assumes_zero(j, runtime_add_mport(j->j_port, job_server)) != KERN_SUCCESS) { goto out_bad2; } - if (!job_assumes(j, launchd_mport_notify_req(j->j_port, MACH_NOTIFY_NO_SENDERS) == KERN_SUCCESS)) { - (void)job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS); + if (job_assumes_zero(j, launchd_mport_notify_req(j->j_port, MACH_NOTIFY_NO_SENDERS)) != KERN_SUCCESS) { + (void)job_assumes_zero(j, launchd_mport_close_recv(j->j_port)); goto out_bad; } return true; out_bad2: - (void)job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_close_recv(j->j_port)); out_bad: return false; } @@ -1533,7 +1542,7 @@ kern_return_t job_setup_exit_port(job_t j) { kern_return_t kr = launchd_mport_create_recv(&j->exit_status_port); - if (!job_assumes(j, kr == KERN_SUCCESS)) { + if (job_assumes_zero(j, kr) != KERN_SUCCESS) { return MACH_PORT_NULL; } @@ -1541,11 +1550,11 @@ job_setup_exit_port(job_t j) .mpl_qlimit = 1, }; kr = mach_port_set_attributes(mach_task_self(), j->exit_status_port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, sizeof(limits)); - (void)job_assumes(j, kr == KERN_SUCCESS); + (void)job_assumes_zero(j, kr); kr = launchd_mport_make_send_once(j->exit_status_port, &j->exit_status_dest); - if (!job_assumes(j, kr == KERN_SUCCESS)) { - (void)job_assumes(j, launchd_mport_close_recv(j->exit_status_port) == KERN_SUCCESS); + if (job_assumes_zero(j, kr) != KERN_SUCCESS) { + (void)job_assumes_zero(j, launchd_mport_close_recv(j->exit_status_port)); j->exit_status_port = MACH_PORT_NULL; } @@ -1558,15 +1567,14 @@ job_new_via_mach_init(job_t j, const char *cmd, uid_t uid, bool ond) const char **argv = (const char **)mach_cmd2argv(cmd); job_t jr = NULL; - if (!job_assumes(j, argv != NULL)) { + if (!argv) { goto out_bad; } jr = job_new(j->mgr, AUTO_PICK_LEGACY_LABEL, NULL, argv); - free(argv); - /* jobs can easily be denied creation during shutdown */ + // Job creation can be denied during shutdown. if (unlikely(jr == NULL)) { goto out_bad; } @@ -1575,7 +1583,7 @@ job_new_via_mach_init(job_t j, const char *cmd, uid_t uid, bool ond) jr->ondemand = ond; jr->legacy_mach_job = true; jr->abandon_pg = true; - jr->priv_port_has_senders = true; /* the IPC that called us will make-send on this port */ + jr->priv_port_has_senders = true; // the IPC that called us will make-send on this port if (!job_setup_machport(jr)) { goto out_bad; @@ -1601,28 +1609,32 @@ job_new_anonymous(jobmgr_t jm, pid_t anonpid) uid_t kp_euid, kp_uid, kp_svuid; gid_t kp_egid, kp_gid, kp_svgid; - if (!jobmgr_assumes(jm, anonpid != 0)) { + if (anonpid == 0) { errno = EINVAL; return NULL; } - - if (!jobmgr_assumes(jm, anonpid < 100000)) { - /* The kernel current defines PID_MAX to be 99999, but that define isn't exported */ + + if (anonpid >= 100000) { + /* The kernel current defines PID_MAX to be 99999, but that define isn't + * exported. + */ + launchd_syslog(LOG_WARNING, "Did PID_MAX change? Got request from PID: %d", anonpid); errno = EINVAL; return NULL; } /* libproc returns the number of bytes written into the buffer upon success, - * zero on failure. + * zero on failure. I'd much rather it return -1 on failure, like sysctl(3). */ if (proc_pidinfo(anonpid, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) { if (errno != ESRCH) { - (void)jobmgr_assumes(jm, errno == 0); + (void)jobmgr_assumes_zero(jm, errno); } return NULL; } - if (!jobmgr_assumes(jm, proc.pbsi_comm[0] != '\0')) { + if (proc.pbsi_comm[0] == '\0') { + launchd_syslog(LOG_WARNING, "Blank command for PID: %d", anonpid); errno = EINVAL; return NULL; } @@ -1647,74 +1659,87 @@ job_new_anonymous(jobmgr_t jm, pid_t anonpid) kp_euid, kp_uid, kp_svuid, kp_egid, kp_gid, kp_svgid, anonpid, proc.pbsi_comm); } - /* "Fix" for a problem that shouldn't even exist. - * See rdar://problem/7264615 for the symptom and rdar://problem/5020256 + /* "Fix" for when the kernel turns the process tree into a weird, cyclic + * graph. + * + * See for the symptom and * as to why this can happen. */ - if (!jobmgr_assumes(jm, (pid_t)proc.pbsi_ppid != anonpid)) { - jobmgr_log(jm, LOG_WARNING, "Process has become its own parent through ptrace(3). It should find a different way to do whatever it's doing. Setting PPID to 0: %s", proc.pbsi_comm); + if ((pid_t)proc.pbsi_ppid == anonpid) { + jobmgr_log(jm, LOG_WARNING, "Process has become its own parent through ptrace(3). Ignoring: %s", proc.pbsi_comm); errno = EINVAL; return NULL; } - /* A total hack: Normally, job_new() returns an error during shutdown, but anonymous jobs are special. */ + /* HACK: Normally, job_new() returns an error during shutdown, but anonymous + * jobs can pop up during shutdown and need to talk to us. + */ if (unlikely(shutdown_state = jm->shutting_down)) { jm->shutting_down = false; } - /* We only set requestor_pid for XPC domains. */ + // We only set requestor_pid for XPC domains. const char *whichlabel = (jm->req_pid == anonpid) ? AUTO_PICK_XPC_LABEL : AUTO_PICK_ANONYMOUS_LABEL; - if (jobmgr_assumes(jm, (jr = job_new(jm, whichlabel, proc.pbsi_comm, NULL)) != NULL)) { + if ((jr = job_new(jm, whichlabel, proc.pbsi_comm, NULL))) { u_int proc_fflags = NOTE_EXEC|NOTE_FORK|NOTE_EXIT; total_anon_children++; jr->anonymous = true; jr->p = anonpid; - /* anonymous process reaping is messy */ + // Anonymous process reaping is messy. LIST_INSERT_HEAD(&jm->active_jobs[ACTIVE_JOB_HASH(jr->p)], jr, pid_hash_sle); - if (unlikely(kevent_mod(jr->p, EVFILT_PROC, EV_ADD, proc_fflags, 0, root_jobmgr) == -1) && job_assumes(jr, errno == ESRCH)) { - /* zombies are weird */ + if (unlikely(kevent_mod(jr->p, EVFILT_PROC, EV_ADD, proc_fflags, 0, root_jobmgr) == -1)) { + if (errno != ESRCH) { + (void)job_assumes_zero(jr, errno); + } + + // Zombies interact weirdly with kevent(3). job_log(jr, LOG_ERR, "Failed to add kevent for PID %u. Will unload at MIG return", jr->p); jr->unload_at_mig_return = true; } if (unlikely(shutdown_state)) { - job_log(jr, LOG_SCOLDING, "This process showed up to the party while all the guests were leaving. Odds are that it will have a miserable time."); + job_log(jr, LOG_APPLEONLY, "This process showed up to the party while all the guests were leaving. Odds are that it will have a miserable time."); } 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); } + // Undo our hack from above. if (unlikely(shutdown_state)) { jm->shutting_down = true; } - /* This is down here to mitigate the effects of rdar://problem/7264615, in which a process - * attaches to its own parent. We need to make sure that the anonymous job has been added - * to the process list so that, if it's used ptrace(3) to cause a cycle in the process - * tree (thereby making it not a tree anymore), we'll find the tracing parent PID of the - * parent process, which is the child, when we go looking for it in jobmgr_find_by_pid(). + /* This is down here to prevent infinite recursion due to a process + * attaching to its parent through ptrace(3) -- causing a cycle in the + * process tree and thereby not making it a tree anymore. We need to make + * sure that the anonymous job has been added to the process list so that + * we'll find the tracing parent PID of the parent process, which is the + * child, when we go looking for it in jobmgr_find_by_pid(). + * + * */ switch (proc.pbsi_ppid) { - case 0: - /* the kernel */ + case 0: + // The kernel. + break; + case 1: + if (!pid1_magic) { break; - case 1: - if (!pid1_magic) { - /* we cannot possibly find a parent job_t that is useful in this function */ - break; - } - /* fall through */ - default: - jp = jobmgr_find_by_pid(jm, proc.pbsi_ppid, true); - if (jobmgr_assumes(jm, jp != NULL)) { - if (jp && !jp->anonymous && unlikely(!(proc.pbsi_flags & P_EXEC))) { - job_log(jp, LOG_DEBUG, "Called *fork(). Please switch to posix_spawn*(), pthreads or launchd. Child PID %u", proc.pbsi_pid); - } + } + // Fall through. + default: + jp = jobmgr_find_by_pid(jm, proc.pbsi_ppid, true); + if (jobmgr_assumes(jm, jp != NULL)) { + if (jp && !jp->anonymous && unlikely(!(proc.pbsi_flags & P_EXEC))) { + job_log(jp, LOG_DEBUG, "Called *fork(). Please switch to posix_spawn*(), pthreads or launchd. Child PID %u", proc.pbsi_pid); } - break; + } + break; } return jr; @@ -1729,16 +1754,16 @@ job_new_subjob(job_t j, uuid_t identifier) size_t label_sz = snprintf(label, 0, "%s.%s", j->label, idstr); job_t nj = (struct job_s *)calloc(1, sizeof(struct job_s) + label_sz + 1); - if (launchd_assumes(nj != NULL)) { + if (nj != NULL) { nj->kqjob_callback = job_callback; nj->mgr = j->mgr; nj->min_run_time = j->min_run_time; nj->timeout = j->timeout; nj->exit_timeout = j->exit_timeout; - + snprintf((char *)nj->label, label_sz + 1, "%s.%s", j->label, idstr); - - /* Set all our simple Booleans that are applicable. */ + + // Set all our simple Booleans that are applicable. nj->debug = j->debug; nj->ondemand = j->ondemand; nj->checkedin = true; @@ -1750,48 +1775,30 @@ job_new_subjob(job_t j, uuid_t identifier) nj->abandon_pg = j->abandon_pg; nj->ignore_pg_at_shutdown = j->ignore_pg_at_shutdown; nj->deny_job_creation = j->deny_job_creation; - nj->kill_via_shmem = j->kill_via_shmem; + nj->enable_transactions = j->enable_transactions; nj->needs_kickoff = j->needs_kickoff; nj->currently_ignored = true; nj->dedicated_instance = true; nj->xpc_service = j->xpc_service; nj->xpc_bootstrapper = j->xpc_bootstrapper; - + nj->mask = j->mask; uuid_copy(nj->instance_id, identifier); - - /* These jobs are purely on-demand Mach jobs. */ - - /* {Hard | Soft}ResourceLimits are not supported. */ - - struct machservice *msi = NULL; - SLIST_FOREACH(msi, &j->machservices, sle) { - /* Only copy MachServices that were actually declared in the plist. - * So skip over per-PID ones and ones that were created via - * bootstrap_register(). - */ - if (msi->upfront) { - mach_port_t mp = MACH_PORT_NULL; - struct machservice *msj = machservice_new(nj, msi->name, &mp, msi->per_pid); - if (job_assumes(nj, 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; - } - } - } - + + // These jobs are purely on-demand Mach jobs. + // {Hard | Soft}ResourceLimits are not supported. + // JetsamPriority is not supported. + if (j->prog) { nj->prog = strdup(j->prog); } if (j->argv) { size_t sz = malloc_size(j->argv); nj->argv = (char **)malloc(sz); - if (job_assumes(nj, nj->argv != NULL)) { - /* This is the start of our strings. */ + if (nj->argv != NULL) { + // This is the start of our strings. char *p = ((char *)nj->argv) + ((j->argc + 1) * sizeof(char *)); - + size_t i = 0; for (i = 0; i < j->argc; i++) { (void)strcpy(p, j->argv[i]); @@ -1799,20 +1806,46 @@ job_new_subjob(job_t j, uuid_t identifier) p += (strlen(j->argv[i]) + 1); } nj->argv[i] = NULL; + } else { + (void)job_assumes_zero(nj, errno); } - + nj->argc = j->argc; } - - /* We ignore global environment variables. */ + + struct machservice *msi = NULL; + SLIST_FOREACH(msi, &j->machservices, sle) { + /* Only copy MachServices that were actually declared in the plist. + * So skip over per-PID ones and ones that were created via + * bootstrap_register(). + */ + if (msi->upfront) { + mach_port_t mp = MACH_PORT_NULL; + struct machservice *msj = machservice_new(nj, msi->name, &mp, msi->per_pid); + 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; + } else { + (void)job_assumes_zero(nj, errno); + } + } + } + + // We ignore global environment variables. struct envitem *ei = NULL; SLIST_FOREACH(ei, &j->env, sle) { - (void)job_assumes(nj, envitem_new(nj, ei->key, ei->value, false, false)); + if (envitem_new(nj, ei->key, ei->value, false)) { + (void)job_assumes_zero(nj, errno); + } } uuid_string_t val; uuid_unparse(identifier, val); - (void)job_assumes(nj, envitem_new(nj, LAUNCH_ENV_INSTANCEID, val, false, false)); - + if (envitem_new(nj, LAUNCH_ENV_INSTANCEID, val, false)) { + (void)job_assumes_zero(nj, errno); + } + if (j->rootdir) { nj->rootdir = strdup(j->rootdir); } @@ -1825,8 +1858,10 @@ job_new_subjob(job_t j, uuid_t identifier) if (j->groupname) { nj->groupname = strdup(j->groupname); } - /* FIXME: We shouldn't redirect all the output from these jobs to the same - * file. We should uniquify the file names. + + /* FIXME: We shouldn't redirect all the output from these jobs to the + * same file. We should uniquify the file names. But this hasn't shown + * to be a problem in practice. */ if (j->stdinpath) { nj->stdinpath = strdup(j->stdinpath); @@ -1840,43 +1875,45 @@ job_new_subjob(job_t j, uuid_t identifier) if (j->alt_exc_handler) { nj->alt_exc_handler = strdup(j->alt_exc_handler); } - #if HAVE_SANDBOX +#if HAVE_SANDBOX if (j->seatbelt_profile) { nj->seatbelt_profile = strdup(j->seatbelt_profile); } - #endif - - #if HAVE_QUARANTINE +#endif + +#if HAVE_QUARANTINE if (j->quarantine_data) { nj->quarantine_data = strdup(j->quarantine_data); } nj->quarantine_data_sz = j->quarantine_data_sz; - #endif +#endif if (j->j_binpref) { size_t sz = malloc_size(j->j_binpref); nj->j_binpref = (cpu_type_t *)malloc(sz); - if (job_assumes(nj, nj->j_binpref)) { + if (nj->j_binpref) { memcpy(&nj->j_binpref, &j->j_binpref, sz); + } else { + (void)job_assumes_zero(nj, errno); } } - - /* JetsamPriority is unsupported. */ - + if (j->asport != MACH_PORT_NULL) { - (void)job_assumes(nj, launchd_mport_copy_send(j->asport) == KERN_SUCCESS); + (void)job_assumes_zero(nj, launchd_mport_copy_send(j->asport)); nj->asport = j->asport; } - + LIST_INSERT_HEAD(&nj->mgr->jobs, nj, sle); - + jobmgr_t where2put = root_jobmgr; if (j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN) { where2put = j->mgr; } 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); } - + return nj; } @@ -1892,7 +1929,7 @@ job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *arg size_t i, cc = 0; job_t j; - launchd_assert(offsetof(struct job_s, kqjob_callback) == 0); + __OSX_COMPILETIME_ASSERT__(offsetof(struct job_s, kqjob_callback) == 0); if (unlikely(jm->shutting_down)) { errno = EINVAL; @@ -1904,17 +1941,26 @@ job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *arg return NULL; } + /* I'd really like to redo this someday. Anonymous jobs carry all the + * baggage of managed jobs with them, even though most of it is unused. + * Maybe when we have Objective-C objects in libSystem, there can be a base + * job type that anonymous and managed jobs inherit from... + */ char *anon_or_legacy = (label == AUTO_PICK_ANONYMOUS_LABEL) ? "anonymous" : "mach_init"; if (unlikely(label == AUTO_PICK_LEGACY_LABEL || label == AUTO_PICK_ANONYMOUS_LABEL)) { if (prog) { bn = prog; } else { strlcpy(tmp_path, argv[0], sizeof(tmp_path)); - bn = basename(tmp_path); /* prog for auto labels is kp.kp_kproc.p_comm */ + // prog for auto labels is kp.kp_kproc.p_comm. + bn = basename(tmp_path); } - snprintf(auto_label, sizeof(auto_label), "%s.%s.%s", sizeof(void *) == 8 ? "0xdeadbeeffeedface" : "0xbabecafe", anon_or_legacy, bn); + + (void)snprintf(auto_label, sizeof(auto_label), "%s.%s.%s", sizeof(void *) == 8 ? "0xdeadbeeffeedface" : "0xbabecafe", anon_or_legacy, bn); label = auto_label; - /* This is so we can do gross things later. See NOTE_EXEC for anonymous jobs */ + /* This is so we can do gross things later. See NOTE_EXEC for anonymous + * jobs. + */ minlabel_len = strlen(label) + MAXCOMLEN; } else { if (label == AUTO_PICK_XPC_LABEL) { @@ -1926,15 +1972,17 @@ 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 (!jobmgr_assumes(jm, j != NULL)) { + if (!j) { + (void)osx_assumes_zero(errno); return NULL; } if (unlikely(label == auto_label)) { - snprintf((char *)j->label, strlen(label) + 1, "%p.%s.%s", j, anon_or_legacy, bn); + (void)snprintf((char *)j->label, strlen(label) + 1, "%p.%s.%s", j, anon_or_legacy, bn); } else { - strcpy((char *)j->label, (label == AUTO_PICK_XPC_LABEL) ? auto_label : label); + (void)strcpy((char *)j->label, (label == AUTO_PICK_XPC_LABEL) ? auto_label : label); } + j->kqjob_callback = job_callback; j->mgr = jm; j->min_run_time = LAUNCHD_MIN_JOB_RUN_TIME; @@ -1943,14 +1991,28 @@ job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *arg j->currently_ignored = true; j->ondemand = true; j->checkedin = true; - j->jetsam_priority = -1; + j->jetsam_priority = DEFAULT_JETSAM_PRIORITY; j->jetsam_memlimit = -1; - j->jetsam_seq = -1; uuid_clear(j->expected_audit_uuid); - +#if TARGET_OS_EMBEDDED + /* Run embedded daemons as background by default. SpringBoard jobs are + * Interactive by default. Unfortunately, so many daemons have opted into + * this priority band that its usefulness is highly questionable. + * + * See . + */ + if (launchd_embedded_handofgod) { + j->pstype = POSIX_SPAWN_IOS_INTERACTIVE; + j->app = true; + } else { + j->pstype = POSIX_SPAWN_IOS_APPLE_DAEMON_START; + } +#endif + if (prog) { j->prog = strdup(prog); - if (!job_assumes(j, j->prog != NULL)) { + if (!j->prog) { + (void)osx_assumes_zero(errno); goto out_bad; } } @@ -1965,8 +2027,8 @@ job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *arg } j->argv = malloc((j->argc + 1) * sizeof(char *) + cc); - - if (!job_assumes(j, j->argv != NULL)) { + if (!j->argv) { + (void)job_assumes_zero(j, errno); goto out_bad; } @@ -1974,18 +2036,19 @@ job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *arg for (i = 0; i < j->argc; i++) { j->argv[i] = co; - strcpy(co, argv[i]); + (void)strcpy(co, argv[i]); co += strlen(argv[i]) + 1; } j->argv[i] = NULL; } + // Sssshhh... don't tell anyone. if (strcmp(j->label, "com.apple.WindowServer") == 0) { j->has_console = true; } LIST_INSERT_HEAD(&jm->jobs, j, sle); - + jobmgr_t where2put_label = root_jobmgr; if (j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN) { where2put_label = j->mgr; @@ -2006,46 +2069,47 @@ out_bad: return NULL; } -#ifndef __LAUNCH_DISABLE_XPC_SUPPORT__ job_t job_new_alias(jobmgr_t jm, job_t src) { - job_t j = NULL; if (job_find(jm, src->label)) { errno = EEXIST; - } else { - j = calloc(1, sizeof(struct job_s) + strlen(src->label) + 1); - if (jobmgr_assumes(jm, j != NULL)) { - strcpy((char *)j->label, src->label); - LIST_INSERT_HEAD(&jm->jobs, j, sle); - LIST_INSERT_HEAD(&jm->label_hash[hash_label(j->label)], j, label_hash_sle); - /* Bad jump address. The kqueue callback for aliases should never be - * invoked. - */ - j->kqjob_callback = (kq_callback)0xfa1afe1; - j->alias = src; - j->mgr = jm; - - struct machservice *msi = NULL; - SLIST_FOREACH(msi, &src->machservices, sle) { - if (!machservice_new_alias(j, msi)) { - jobmgr_log(jm, LOG_ERR, "Failed to alias job: %s", src->label); - errno = EINVAL; - job_remove(j); - j = NULL; - break; - } - } - } + return NULL; + } - if (j) { - job_log(j, LOG_DEBUG, "Aliased service into domain: %s", jm->name); + job_t j = calloc(1, sizeof(struct job_s) + strlen(src->label) + 1); + if (!j) { + (void)osx_assumes_zero(errno); + return NULL; + } + + (void)strcpy((char *)j->label, src->label); + LIST_INSERT_HEAD(&jm->jobs, j, sle); + LIST_INSERT_HEAD(&jm->label_hash[hash_label(j->label)], j, label_hash_sle); + /* Bad jump address. The kqueue callback for aliases should never be + * invoked. + */ + j->kqjob_callback = (kq_callback)0xfa1afe1; + j->alias = src; + j->mgr = jm; + + struct machservice *msi = NULL; + SLIST_FOREACH(msi, &src->machservices, sle) { + if (!machservice_new_alias(j, msi)) { + jobmgr_log(jm, LOG_ERR, "Failed to alias job: %s", src->label); + errno = EINVAL; + job_remove(j); + j = NULL; + break; } } + if (j) { + job_log(j, LOG_DEBUG, "Aliased service into domain: %s", jm->name); + } + return j; } -#endif /* __LAUNCH_DISABLE_XPC_SUPPORT__ */ job_t job_import(launch_data_t pload) @@ -2056,9 +2120,10 @@ job_import(launch_data_t pload) return NULL; } - /* Since jobs are effectively stalled until they get security sessions assigned - * to them, we may wish to reconsider this behavior of calling the job "enabled" - * as far as other jobs with the OtherJobEnabled KeepAlive criterion set. + /* Since jobs are effectively stalled until they get security sessions + * assigned to them, we may wish to reconsider this behavior of calling the + * job "enabled" as far as other jobs with the OtherJobEnabled KeepAlive + * criterion set. */ job_dispatch_curious_jobs(j); return job_dispatch(j, false); @@ -2140,7 +2205,7 @@ job_import_bool(job_t j, const char *key, bool value) case 'h': case 'H': if (strcasecmp(key, LAUNCH_JOBKEY_HOPEFULLYEXITSLAST) == 0) { - job_log(j, LOG_INFO, "%s has been deprecated. Please use the new %s key instead and add EnableTransactions to your launchd.plist.", LAUNCH_JOBKEY_HOPEFULLYEXITSLAST, LAUNCH_JOBKEY_BEGINTRANSACTIONATSHUTDOWN); + job_log(j, LOG_PERF, "%s has been deprecated. Please use the new %s key instead and add EnableTransactions to your launchd.plist.", LAUNCH_JOBKEY_HOPEFULLYEXITSLAST, LAUNCH_JOBKEY_BEGINTRANSACTIONATSHUTDOWN); j->dirty_at_shutdown = value; found_key = true; } @@ -2154,14 +2219,14 @@ job_import_bool(job_t j, const char *key, bool value) j->start_on_mount = value; found_key = true; } else if (strcasecmp(key, LAUNCH_JOBKEY_SERVICEIPC) == 0) { - /* this only does something on Mac OS X 10.4 "Tiger" */ + // this only does something on Mac OS X 10.4 "Tiger" found_key = true; } else if (strcasecmp(key, LAUNCH_JOBKEY_SHUTDOWNMONITOR) == 0) { - if (_s_shutdown_monitor) { + if (_launchd_shutdown_monitor) { job_log(j, LOG_ERR, "Only one job may monitor shutdown."); } else { j->shutdown_monitor = true; - _s_shutdown_monitor = j; + _launchd_shutdown_monitor = j; } found_key = true; } @@ -2204,7 +2269,7 @@ job_import_bool(job_t j, const char *key, bool value) case 'R': if (strcasecmp(key, LAUNCH_JOBKEY_RUNATLOAD) == 0) { if (value) { - /* We don't want value == false to change j->start_pending */ + // We don't want value == false to change j->start_pending j->start_pending = true; } found_key = true; @@ -2216,27 +2281,32 @@ job_import_bool(job_t j, const char *key, bool value) j->globargv = value; found_key = true; } else if (strcasecmp(key, LAUNCH_JOBKEY_ENABLETRANSACTIONS) == 0) { - j->kill_via_shmem = value; + j->enable_transactions = value; found_key = true; } else if (strcasecmp(key, LAUNCH_JOBKEY_ENTERKERNELDEBUGGERBEFOREKILL) == 0) { j->debug_before_kill = value; found_key = true; } else if (strcasecmp(key, LAUNCH_JOBKEY_EMBEDDEDPRIVILEGEDISPENSATION) == 0) { - if (!s_embedded_privileged_job) { - j->embedded_special_privileges = value; - s_embedded_privileged_job = j; +#if TARGET_OS_EMBEDDED + if (!_launchd_embedded_god) { + if ((j->embedded_god = value)) { + _launchd_embedded_god = 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 found_key = true; } else if (strcasecmp(key, LAUNCH_JOBKEY_EVENTMONITOR) == 0) { - if (job_assumes(j, _s_event_monitor == NULL)) { + if (!_launchd_event_monitor) { j->event_monitor = value; if (value) { - _s_event_monitor = j; + _launchd_event_monitor = j; } } else { - job_log(j, LOG_NOTICE, "Job tried to steal event monitoring responsibility!"); + job_log(j, LOG_NOTICE, "Job tried to steal event monitoring responsibility from: %s", _launchd_event_monitor->label); } found_key = true; } @@ -2252,10 +2322,10 @@ job_import_bool(job_t j, const char *key, bool value) case 'X': if (strcasecmp(key, LAUNCH_JOBKEY_XPCDOMAINBOOTSTRAPPER) == 0) { if (pid1_magic) { - if (_s_xpc_bootstrapper) { - job_log(j, LOG_ERR, "This job tried to steal the XPC domain bootstrapper property from the following job: %s", _s_xpc_bootstrapper->label); + if (_launchd_xpc_bootstrapper) { + job_log(j, LOG_ERR, "This job tried to steal the XPC domain bootstrapper property from the following job: %s", _launchd_xpc_bootstrapper->label); } else { - _s_xpc_bootstrapper = j; + _launchd_xpc_bootstrapper = j; j->xpc_bootstrapper = value; } } else { @@ -2291,16 +2361,28 @@ job_import_string(job_t j, const char *key, const char *value) 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) { - j->pstype = POSIX_SPAWN_OSX_WIDGET_START; - } +#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 - else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_IOSAPP) == 0) { j->pstype = POSIX_SPAWN_IOS_APP_START; - } -#endif /* TARGET_OS_EMBEDDED */ - else { +#endif + } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_INTERACTIVE) == 0) { +#if TARGET_OS_EMBEDDED + j->pstype = POSIX_SPAWN_IOS_INTERACTIVE; +#endif + } 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. + } else { job_log(j, LOG_ERR, "Unknown value for key %s: %s", key, value); } return; @@ -2367,11 +2449,11 @@ job_import_string(job_t j, const char *key, const char *value) } else if (strcasecmp(key, LAUNCH_JOBKEY_STANDARDINPATH) == 0) { where2put = &j->stdinpath; j->stdin_fd = _fd(open(value, O_RDONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, DEFFILEMODE)); - if (job_assumes(j, j->stdin_fd != -1)) { - /* open() should not block, but regular IO by the job should */ - (void)job_assumes(j, fcntl(j->stdin_fd, F_SETFL, 0) != -1); - /* XXX -- EV_CLEAR should make named pipes happy? */ - (void)job_assumes(j, kevent_mod(j->stdin_fd, EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, j) != -1); + if (job_assumes_zero_p(j, j->stdin_fd) != -1) { + // open() should not block, but regular IO by the job should + (void)job_assumes_zero_p(j, fcntl(j->stdin_fd, F_SETFL, 0)); + // XXX -- EV_CLEAR should make named pipes happy? + (void)job_assumes_zero_p(j, kevent_mod(j->stdin_fd, EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, j)); } else { j->stdin_fd = 0; } @@ -2393,10 +2475,15 @@ job_import_string(job_t j, const char *key, const char *value) } if (likely(where2put)) { - (void)job_assumes(j, (*where2put = strdup(value)) != NULL); + if (!(*where2put = strdup(value))) { + (void)job_assumes_zero(j, errno); + } } else { - /* See rdar://problem/5496612. These two are okay. */ - if (strncmp(key, "SHAuthorizationRight", sizeof("SHAuthorizationRight")) != 0 && strncmp(key, "ServiceDescription", sizeof("ServiceDescription")) != 0) { + // See rdar://problem/5496612. These two are okay. + if (strncmp(key, "SHAuthorizationRight", sizeof("SHAuthorizationRight")) == 0 + || strncmp(key, "ServiceDescription", sizeof("ServiceDescription")) == 0) { + job_log(j, LOG_APPLEONLY, "This key is no longer relevant and should be removed: %s", key); + } else { job_log(j, LOG_WARNING, "Unknown key: %s", key); } } @@ -2424,7 +2511,7 @@ job_import_integer(job_t j, const char *key, long long value) case 'J': if (strcasecmp(key, LAUNCH_JOBKEY_JETSAMPRIORITY) == 0) { job_log(j, LOG_WARNING | LOG_CONSOLE, "Please change the JetsamPriority key to be in a dictionary named JetsamProperties."); - + launch_data_t pri = launch_data_new_integer(value); if (job_assumes(j, pri != NULL)) { jetsam_property_setup(pri, LAUNCH_JOBKEY_JETSAMPRIORITY, j); @@ -2482,7 +2569,7 @@ job_import_integer(job_t j, const char *key, long long value) runtime_add_weak_ref(); j->start_interval = (typeof(j->start_interval)) value; - (void)job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, j->start_interval, j) != -1); + (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, j->start_interval, j)); } #if HAVE_SANDBOX } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXFLAGS) == 0) { @@ -2498,8 +2585,7 @@ job_import_integer(job_t j, const char *key, long long value) } void -job_import_opaque(job_t j __attribute__((unused)), - const char *key, launch_data_t value __attribute__((unused))) +job_import_opaque(job_t j __attribute__((unused)), const char *key, launch_data_t value __attribute__((unused))) { switch (key[0]) { case 'q': @@ -2649,7 +2735,6 @@ void job_import_array(job_t j, const char *key, launch_data_t value) { size_t i, value_cnt = launch_data_array_get_count(value); - const char *str; switch (key[0]) { case 'p': @@ -2669,34 +2754,9 @@ job_import_array(job_t j, const char *key, launch_data_t value) return; } break; - case 'q': - case 'Q': - if (strcasecmp(key, LAUNCH_JOBKEY_QUEUEDIRECTORIES) == 0) { - for (i = 0; i < value_cnt; i++) { - str = launch_data_get_string(launch_data_array_get_index(value, i)); - if (job_assumes(j, str != NULL)) { - semaphoreitem_new(j, DIR_NOT_EMPTY, str); - } - } - - } - break; - case 'w': - case 'W': - if (strcasecmp(key, LAUNCH_JOBKEY_WATCHPATHS) == 0) { - for (i = 0; i < value_cnt; i++) { - str = launch_data_get_string(launch_data_array_get_index(value, i)); - if (job_assumes(j, str != NULL)) { - semaphoreitem_new(j, PATH_CHANGES, str); - } - } - } - break; case 'b': case 'B': - if (strcasecmp(key, LAUNCH_JOBKEY_BONJOURFDS) == 0) { - socketgroup_setup(value, LAUNCH_JOBKEY_BONJOURFDS, j); - } else if (strcasecmp(key, LAUNCH_JOBKEY_BINARYORDERPREFERENCE) == 0) { + if (strcasecmp(key, LAUNCH_JOBKEY_BINARYORDERPREFERENCE) == 0) { if (job_assumes(j, j->j_binpref = malloc(value_cnt * sizeof(*j->j_binpref)))) { j->j_binpref_cnt = value_cnt; for (i = 0; i < value_cnt; i++) { @@ -2725,7 +2785,8 @@ job_import_keys(launch_data_t obj, const char *key, void *context) job_t j = context; launch_data_type_t kind; - if (!launchd_assumes(obj != NULL)) { + if (!obj) { + launchd_syslog(LOG_ERR, "NULL object given to job_import_keys()."); return; } @@ -2790,12 +2851,12 @@ jobmgr_import2(jobmgr_t jm, launch_data_t pload) } #if TARGET_OS_EMBEDDED - if (unlikely(g_embedded_privileged_action && s_embedded_privileged_job)) { + if (unlikely(launchd_embedded_handofgod && _launchd_embedded_god)) { if (unlikely(!(tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_USERNAME)))) { errno = EPERM; return NULL; } - + const char *username = NULL; if (likely(tmp && launch_data_get_type(tmp) == LAUNCH_DATA_STRING)) { username = launch_data_get_string(tmp); @@ -2803,24 +2864,24 @@ jobmgr_import2(jobmgr_t jm, launch_data_t pload) errno = EPERM; return NULL; } - - if (!jobmgr_assumes(jm, s_embedded_privileged_job->username != NULL && username != NULL)) { + + if (!jobmgr_assumes(jm, _launchd_embedded_god->username != NULL && username != NULL)) { errno = EPERM; return NULL; } - - if (unlikely(strcmp(s_embedded_privileged_job->username, username) != 0)) { + + if (unlikely(strcmp(_launchd_embedded_god->username, username) != 0)) { errno = EPERM; return NULL; } - } else if (g_embedded_privileged_action) { + } else if (launchd_embedded_handofgod) { errno = EINVAL; return NULL; } #endif - if ((tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAM)) && - (launch_data_get_type(tmp) == LAUNCH_DATA_STRING)) { + if ((tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAM)) + && (launch_data_get_type(tmp) == LAUNCH_DATA_STRING)) { prog = launch_data_get_string(tmp); } @@ -2894,6 +2955,7 @@ jobmgr_import2(jobmgr_t jm, launch_data_t pload) * treated as a singleton, so just return the existing one so that * it may be aliased into the requesting process' XPC domain. */ + errno = EEXIST; return j; } else { /* If we're not a global XPC domain, then it's an error to try @@ -2921,28 +2983,29 @@ jobmgr_import2(jobmgr_t jm, launch_data_t pload) j->asport = MACH_PORT_NULL; } + if (pid1_magic && !jm->parentmgr) { + /* Workaround reentrancy in CF. We don't make this a global variable + * because we don't want per-user launchd's to inherit it. So we + * just set it for every job that we import into the System session. + * + * See . + */ + envitem_new(j, "__CF_USER_TEXT_ENCODING", "0x0:0:0", false); + } + if (j->event_monitor) { - if (job_assumes(j, LIST_FIRST(&j->events) == NULL)) { - struct machservice *msi = NULL; - SLIST_FOREACH(msi, &j->machservices, sle) { - if (msi->event_update_port) { - break; - } - } + eventsystem_ping(); + } - if (job_assumes(j, msi != NULL)) { - /* Create our send-once right so we can kick things off. */ - (void)job_assumes(j, launchd_mport_make_send_once(msi->port, &_s_event_update_port) == KERN_SUCCESS); - if (!LIST_EMPTY(&_s_event_systems)) { - eventsystem_ping(); - } - } - } else { - job_log(j, LOG_ERR, "The event monitor job may not have a LaunchEvents dictionary."); - job_remove(j); - j = NULL; - } +#if TARGET_OS_EMBEDDED + /* SpringBoard runs at Interactive priority. + * + * See . + */ + if (j->embedded_god) { + j->pstype = POSIX_SPAWN_IOS_INTERACTIVE; } +#endif } return j; @@ -2951,6 +3014,7 @@ jobmgr_import2(jobmgr_t jm, launch_data_t pload) bool jobmgr_label_test(jobmgr_t jm, const char *str) { + char *endstr = NULL; const char *ptr; if (str[0] == '\0') { @@ -2965,8 +3029,15 @@ jobmgr_label_test(jobmgr_t jm, const char *str) } } - if ((strncasecmp(str, "com.apple.launchd", strlen("com.apple.launchd")) == 0) || - (strncasecmp(str, "com.apple.launchctl", strlen("com.apple.launchctl")) == 0)) { + strtoll(str, &endstr, 0); + + if (str != endstr) { + jobmgr_log(jm, LOG_ERR, "Job labels are not allowed to begin with numbers: %s", str); + return false; + } + + if ((strncasecmp(str, "com.apple.launchd", strlen("com.apple.launchd")) == 0) + || (strncasecmp(str, "com.apple.launchctl", strlen("com.apple.launchctl")) == 0)) { jobmgr_log(jm, LOG_ERR, "Job labels are not allowed to use a reserved prefix: %s", str); return false; } @@ -2978,14 +3049,15 @@ job_t job_find(jobmgr_t jm, const char *label) { job_t ji; - + if (!jm) { jm = root_jobmgr; } - + LIST_FOREACH(ji, &jm->label_hash[hash_label(label)], label_hash_sle) { if (unlikely(ji->removal_pending || ji->mgr->shutting_down)) { - continue; /* 5351245 and 5488633 respectively */ + // 5351245 and 5488633 respectively + continue; } if (strcmp(ji->label, label) == 0) { @@ -2997,13 +3069,13 @@ job_find(jobmgr_t jm, const char *label) return NULL; } -/* Should try and consolidate with job_mig_intran2() and jobmgr_find_by_pid(). */ +// Should try and consolidate with job_mig_intran2() and jobmgr_find_by_pid(). job_t jobmgr_find_by_pid_deep(jobmgr_t jm, pid_t p, bool anon_okay) { job_t ji = NULL; LIST_FOREACH(ji, &jm->active_jobs[ACTIVE_JOB_HASH(p)], pid_hash_sle) { - if (ji->p == p && (!ji->anonymous || (ji->anonymous && anon_okay)) ) { + if (ji->p == p && (!ji->anonymous || (ji->anonymous && anon_okay))) { return ji; } } @@ -3067,13 +3139,13 @@ job_mig_intran(mach_port_t p) jr = job_mig_intran2(root_jobmgr, p, ldc->pid); - if (!jobmgr_assumes(root_jobmgr, jr != NULL)) { + if (!jr) { struct proc_bsdshortinfo proc; if (proc_pidinfo(ldc->pid, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) { if (errno != ESRCH) { - (void)jobmgr_assumes(root_jobmgr, errno == 0); + (void)jobmgr_assumes_zero(root_jobmgr, errno); } else { - jobmgr_log(root_jobmgr, LOG_ERR, "%s() was confused by PID %u UID %u EUID %u Mach Port 0x%x: %s", __func__, ldc->pid, ldc->uid, ldc->euid, p, proc.pbsi_comm); + jobmgr_log(root_jobmgr, LOG_ERR, "%s[%i] disappeared out from under us (UID: %u EUID: %u)", proc.pbsi_comm, ldc->pid, ldc->uid, ldc->euid); } } } @@ -3098,12 +3170,10 @@ job_find_by_service_port(mach_port_t p) void job_mig_destructor(job_t j) { - /* - * 5477111 + /* The job can go invalid before this point. * - * 'j' can be invalid at this point. We should fix this up after Leopard ships. + * */ - if (unlikely(j && (j != workaround_5477111) && j->unload_at_mig_return)) { job_log(j, LOG_NOTICE, "Unloading PID %u at MIG return.", j->p); job_remove(j); @@ -3138,8 +3208,10 @@ job_export_all(void) { launch_data_t resp = launch_data_alloc(LAUNCH_DATA_DICTIONARY); - if (launchd_assumes(resp != NULL)) { + if (resp != NULL) { job_export_all2(root_jobmgr, resp); + } else { + (void)osx_assumes_zero(errno); } return resp; @@ -3151,8 +3223,8 @@ job_log_stray_pg(job_t j) pid_t *pids = NULL; size_t len = sizeof(pid_t) * get_kern_max_proc(); int i = 0, kp_cnt = 0; - - if (!do_apple_internal_logging) { + + if (!launchd_apple_internal) { return; } @@ -3161,26 +3233,26 @@ job_log_stray_pg(job_t j) if (!job_assumes(j, (pids = malloc(len)) != NULL)) { return; } - if (!job_assumes(j, (kp_cnt = proc_listpgrppids(j->p, pids, len)) != -1)) { + if (job_assumes_zero_p(j, (kp_cnt = proc_listpgrppids(j->p, pids, len))) == -1) { goto out; } - + for (i = 0; i < kp_cnt; i++) { pid_t p_i = pids[i]; if (p_i == j->p) { continue; - } else if (!job_assumes(j, p_i != 0 && p_i != 1)) { + } else if (p_i == 0 || p_i == 1) { continue; } - + struct proc_bsdshortinfo proc; if (proc_pidinfo(p_i, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) { if (errno != ESRCH) { - job_assumes(j, errno == 0); + (void)job_assumes_zero(j, errno); } continue; } - + pid_t pp_i = proc.pbsi_ppid; const char *z = (proc.pbsi_status == SZOMB) ? "zombie " : ""; const char *n = proc.pbsi_comm; @@ -3198,31 +3270,17 @@ job_reap(job_t j) struct rusage ru; int status; - bool is_system_bootstrapper = j->is_bootstrapper && pid1_magic && !j->mgr->parentmgr; - - job_log(j, LOG_DEBUG, "Reaping"); + bool is_system_bootstrapper = ((j->is_bootstrapper && pid1_magic) && !j->mgr->parentmgr); - if (j->shmem) { - (void)job_assumes(j, vm_deallocate(mach_task_self(), (vm_address_t)j->shmem, getpagesize()) == 0); - j->shmem = NULL; - } + job_log(j, LOG_DEBUG, "Reaping"); if (unlikely(j->weird_bootstrap)) { int64_t junk = 0; job_mig_swap_integer(j, VPROC_GSK_WEIRD_BOOTSTRAP, 0, 0, &junk); } - if (j->log_redirect_fd && !j->legacy_LS_job) { - job_log_stdouterr(j); /* one last chance */ - - if (j->log_redirect_fd) { - (void)job_assumes(j, runtime_close(j->log_redirect_fd) != -1); - j->log_redirect_fd = 0; - } - } - if (j->fork_fd) { - (void)job_assumes(j, runtime_close(j->fork_fd) != -1); + (void)job_assumes_zero_p(j, runtime_close(j->fork_fd)); j->fork_fd = 0; } @@ -3230,18 +3288,19 @@ job_reap(job_t j) status = 0; memset(&ru, 0, sizeof(ru)); } else { - /* - * The job is dead. While the PID/PGID is still known to be - * valid, try to kill abandoned descendant processes. + uint64_t rt = runtime_get_nanoseconds_since(j->start_time); + j->trt += rt; + + job_log(j, LOG_PERF, "Last instance wall time: %06f", (double)rt / (double)NSEC_PER_SEC); + j->nruns++; + + /* The job is dead. While the PID/PGID is still known to be valid, try + * to kill abandoned descendant processes. */ job_log_stray_pg(j); if (!j->abandon_pg) { - if (unlikely(runtime_killpg(j->p, SIGTERM) == -1 && errno != ESRCH)) { -#ifdef __LP64__ + if (unlikely(killpg2(j->p, SIGTERM) == -1 && errno != ESRCH)) { job_log(j, LOG_APPLEONLY, "Bug: 5487498"); -#else - (void)job_assumes(j, false); -#endif } } @@ -3267,21 +3326,41 @@ job_reap(job_t j) * * See . */ + int r = -1; if (j->workaround9359725) { job_log(j, LOG_NOTICE, "Simulated exit: "); status = W_EXITCODE(-1, SIGSEGV); memset(&ru, 0, sizeof(ru)); - } else if (wait4(j->p, &status, 0, &ru) == -1) { + } else if ((r = wait4(j->p, &status, 0, &ru)) == -1) { job_log(j, LOG_NOTICE, "Assuming job exited: : %d: %s", errno, strerror(errno)); status = W_EXITCODE(-1, SIGSEGV); memset(&ru, 0, sizeof(ru)); } + + 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->exit_timeout) { - kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); + (void)kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); } - + LIST_REMOVE(j, pid_hash_sle); if (j->sent_signal_time) { @@ -3295,7 +3374,10 @@ job_reap(job_t j) timeradd(&ru.ru_utime, &j->ru.ru_utime, &j->ru.ru_utime); timeradd(&ru.ru_stime, &j->ru.ru_stime, &j->ru.ru_stime); - j->ru.ru_maxrss += ru.ru_maxrss; + 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; @@ -3309,16 +3391,44 @@ job_reap(job_t j) 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(status); + if (WIFEXITED(status) && exit_status != 0) { + if (!j->did_exec && _launchd_support_system) { + xpc_object_t event = NULL; + switch (exit_status) { + case ENOENT: + case ENOTDIR: + case ESRCH: + job_log(j, LOG_NOTICE, "Job failed to exec(3). Setting up event to tell us when to try again: %d: %s", exit_status, strerror(exit_status)); + event = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_string(event, "Executable", j->prog ? j->prog : j->argv[0]); + if (j->mach_uid) { + xpc_dictionary_set_uint64(event, "UID", j->mach_uid); + } else if (j->username) { + xpc_dictionary_set_string(event, "UserName", j->username); + } - if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { - int level = LOG_WARNING; - if (!j->did_exec && (j->fail_cnt++ % LAUNCHD_LOG_FAILED_EXEC_FREQ) != 0) { - level = LOG_DEBUG; - } + if (j->groupname) { + xpc_dictionary_set_string(event, "GroupName", j->groupname); + } - job_log(j, level, "Exited with code: %d", WEXITSTATUS(status)); - } else { - j->fail_cnt = 0; + (void)externalevent_new(j, _launchd_support_system, j->label, event); + xpc_release(event); + + j->waiting4ok = true; + default: + job_log(j, LOG_NOTICE, "Job failed to exec(3) for weird reason: %d", exit_status); + } + } else { + int level = LOG_INFO; + if (exit_status != 0) { + level = LOG_ERR; + } + + job_log(j, level, "Exited with code: %d", exit_status); + } } if (WIFSIGNALED(status)) { @@ -3327,7 +3437,7 @@ job_reap(job_t j) job_log(j, LOG_NOTICE, "Exited: %s", strsignal(s)); } else if (!j->stopped && !j->clean_kill) { switch (s) { - /* Signals which indicate a crash. */ + // Signals which indicate a crash. case SIGILL: case SIGABRT: case SIGFPE: @@ -3345,7 +3455,7 @@ job_reap(job_t j) job_log(j, LOG_WARNING, "Exited abnormally: %s", strsignal(s)); break; } - + if (is_system_bootstrapper && j->crashed) { job_log(j, LOG_ERR | LOG_CONSOLE, "The %s bootstrapper has crashed: %s", j->mgr->name, strsignal(s)); } @@ -3353,14 +3463,14 @@ job_reap(job_t j) } j->reaped = true; - + struct machservice *msi = NULL; if (j->crashed || !(j->did_exec || j->anonymous)) { SLIST_FOREACH(msi, &j->machservices, sle) { if (j->crashed && !msi->isActive && (msi->drain_one_on_crash || msi->drain_all_on_crash)) { machservice_drain_port(msi); } - + if (!j->did_exec && msi->reset && job_assumes(j, !msi->isActive)) { machservice_resetport(j, msi); } @@ -3377,9 +3487,20 @@ job_reap(job_t j) * not exec(3), then we don't want to continue trying, since there * is very likely a serious configuration error with the service. * + * The above comment is weird. I originally said we should drain + * messages but not reset the port, but that's exactly what we do + * below, and I'm not sure which is the mistake, the comment or the + * actual behavior. + * + * Since it's always been this way, I'll assume that the comment is + * incorrect, but I'll leave it in place just to remind myself to + * actually look into it at some point. + * * */ - machservice_resetport(j, msi); + if (msi->upfront && job_assumes(j, !msi->isActive)) { + machservice_resetport(j, msi); + } } } @@ -3399,7 +3520,7 @@ job_reap(job_t j) if (j->exit_status_dest) { errno = helper_downcall_wait(j->exit_status_dest, j->last_exit_status); if (errno && errno != MACH_SEND_INVALID_DEST) { - (void)job_assumes(j, errno == 0); + (void)job_assumes_zero(j, errno); } j->exit_status_dest = MACH_PORT_NULL; @@ -3414,11 +3535,10 @@ job_reap(job_t j) kern_return_t kr = job_mig_spawn2_reply(j->spawn_reply_port, BOOTSTRAP_SUCCESS, j->p, j->exit_status_port); if (kr) { if (kr != MACH_SEND_INVALID_DEST) { - errno = kr; - (void)job_assumes(j, errno == KERN_SUCCESS); + (void)job_assumes_zero(j, kr); } - (void)job_assumes(j, launchd_mport_close_recv(j->exit_status_port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_close_recv(j->exit_status_port)); } j->exit_status_port = MACH_PORT_NULL; @@ -3428,50 +3548,32 @@ job_reap(job_t j) if (j->anonymous) { total_anon_children--; if (j->holds_ref) { + job_log(j, LOG_PERF, "Anonymous job exited holding reference."); runtime_del_ref(); } } else { + job_log(j, LOG_PERF, "Job exited."); runtime_del_ref(); total_children--; } - + if (j->has_console) { - g_wsp = 0; + launchd_wsp = 0; } if (j->shutdown_monitor) { job_log(j, LOG_NOTICE | LOG_CONSOLE, "Shutdown monitor has exited."); - _s_shutdown_monitor = NULL; + _launchd_shutdown_monitor = NULL; j->shutdown_monitor = false; } - if (j->event_monitor && !j->mgr->shutting_down) { - msi = NULL; - SLIST_FOREACH(msi, &j->machservices, sle) { - if (msi->event_update_port) { - break; - } - } - /* Only do this if we've gotten the port-destroyed notification already. - * If we haven't yet, the port destruction handler will do this. - */ - if (job_assumes(j, msi != NULL) && !msi->isActive) { - if (_s_event_update_port == MACH_PORT_NULL) { - (void)job_assumes(j, launchd_mport_make_send_once(msi->port, &_s_event_update_port) == KERN_SUCCESS); - } - eventsystem_ping(); - } - } - if (!j->anonymous) { j->mgr->normal_active_cnt--; } j->sent_signal_time = 0; j->sent_sigkill = false; j->clean_kill = false; - j->sent_kill_via_shmem = false; - j->lastlookup = NULL; - j->lastlookup_gennum = 0; + j->event_monitor_ready2signal = false; j->p = 0; } @@ -3508,16 +3610,16 @@ job_dispatch_curious_jobs(job_t j) if (!(si->why == OTHER_JOB_ENABLED || si->why == OTHER_JOB_DISABLED)) { continue; } - + if (strcmp(si->what, j->label) == 0) { job_log(ji, LOG_DEBUG, "Dispatching out of interest in \"%s\".", j->label); - + if (!ji->removing) { job_dispatch(ji, false); } else { job_log(ji, LOG_NOTICE, "The following job is circularly dependent upon this one: %s", j->label); } - + /* ji could be removed here, so don't do anything with it or its semaphores * after this point. */ @@ -3530,26 +3632,33 @@ job_dispatch_curious_jobs(job_t j) job_t job_dispatch(job_t j, bool kickstart) { - /* Don't dispatch a job if it has no audit session set. */ + // Don't dispatch a job if it has no audit session set. if (!uuid_is_null(j->expected_audit_uuid)) { + job_log(j, LOG_DEBUG, "Job is still awaiting its audit session UUID. Not dispatching."); return NULL; } if (j->alias) { - j = j->alias; + job_log(j, LOG_DEBUG, "Job is an alias. Not dispatching."); + return NULL; + } + + if (j->waiting4ok) { + job_log(j, LOG_DEBUG, "Job cannot exec(3). Not dispatching."); + return NULL; } #if TARGET_OS_EMBEDDED - if (g_embedded_privileged_action && s_embedded_privileged_job) { - if (!job_assumes(j, s_embedded_privileged_job->username != NULL && j->username != NULL)) { + if (launchd_embedded_handofgod && _launchd_embedded_god) { + if (!job_assumes(j, _launchd_embedded_god->username != NULL && j->username != NULL)) { errno = EPERM; return NULL; } - - if (strcmp(j->username, s_embedded_privileged_job->username) != 0) { + + if (strcmp(j->username, _launchd_embedded_god->username) != 0) { errno = EPERM; return NULL; } - } else if (g_embedded_privileged_action) { + } else if (launchd_embedded_handofgod) { errno = EINVAL; return NULL; } @@ -3565,18 +3674,20 @@ job_dispatch(job_t j, bool kickstart) */ if (!job_active(j)) { if (job_useless(j)) { + job_log(j, LOG_DEBUG, "Job is useless. Removing."); job_remove(j); return NULL; } if (unlikely(j->per_user && j->peruser_suspend_count > 0)) { + job_log(j, LOG_DEBUG, "Per-user launchd is suspended. Not dispatching."); return NULL; } - + if (kickstart || job_keepalive(j)) { - job_log(j, LOG_DEBUG, "Starting job (kickstart = %s)", kickstart ? "true" : "false"); + job_log(j, LOG_DEBUG, "%starting job", kickstart ? "Kicks" : "S"); job_start(j); } else { - job_log(j, LOG_DEBUG, "Watching job (kickstart = %s)", kickstart ? "true" : "false"); + job_log(j, LOG_DEBUG, "Watching job."); job_watch(j); /* @@ -3590,64 +3701,12 @@ job_dispatch(job_t j, bool kickstart) } } } else { - job_log(j, LOG_DEBUG, "Tried to dispatch an already active job (%s).", job_active(j)); + job_log(j, LOG_DEBUG, "Tried to dispatch an already active job: %s.", job_active(j)); } return j; } -void -job_log_stdouterr2(job_t j, const char *msg, ...) -{ - struct runtime_syslog_attr attr = { j->label, j->label, j->mgr->name, LOG_NOTICE, getuid(), j->p, j->p }; - va_list ap; - - va_start(ap, msg); - runtime_vsyslog(&attr, msg, ap); - va_end(ap); -} - -void -job_log_stdouterr(job_t j) -{ - char *msg, *bufindex, *buf = malloc(BIG_PIPE_SIZE + 1); - bool close_log_redir = false; - ssize_t rsz; - - if (!job_assumes(j, buf != NULL)) { - return; - } - - bufindex = buf; - - rsz = read(j->log_redirect_fd, buf, BIG_PIPE_SIZE); - - if (unlikely(rsz == 0)) { - job_log(j, LOG_DEBUG, "Standard out/error pipe closed"); - close_log_redir = true; - } else if (rsz == -1) { - if (!job_assumes(j, errno == EAGAIN)) { - close_log_redir = true; - } - } else { - buf[rsz] = '\0'; - - while ((msg = strsep(&bufindex, "\n\r"))) { - if (msg[0]) { - job_log_stdouterr2(j, "%s", msg); - } - } - } - - free(buf); - - if (unlikely(close_log_redir)) { - (void)job_assumes(j, runtime_close(j->log_redirect_fd) != -1); - j->log_redirect_fd = 0; - job_dispatch(j, false); - } -} - void job_kill(job_t j) { @@ -3655,10 +3714,10 @@ job_kill(job_t j) return; } - (void)job_assumes(j, runtime_kill(j->p, SIGKILL) != -1); + (void)job_assumes_zero_p(j, kill2(j->p, SIGKILL)); j->sent_sigkill = true; - (void)job_assumes(j, kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, LAUNCHD_SIGKILL_TIMER, j) != -1); + (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, LAUNCHD_SIGKILL_TIMER, j)); job_log(j, LOG_DEBUG, "Sent SIGKILL signal"); } @@ -3666,15 +3725,8 @@ job_kill(job_t j) void job_open_shutdown_transaction(job_t j) { - if (j->kill_via_shmem) { - if (j->shmem) { - job_log(j, LOG_DEBUG, "Opening shutdown transaction for job."); - (void)__sync_add_and_fetch(&j->shmem->vp_shmem_transaction_cnt, 1); - } else { - job_log(j, LOG_DEBUG, "Job wants to be dirty at shutdown, but it has not set up shared memory. Treating normally."); - j->dirty_at_shutdown = false; - } - } else { + int rv = proc_set_dirty(j->p, true); + if (rv != 0) { job_log(j, LOG_DEBUG, "Job wants to be dirty at shutdown, but it is not Instant Off-compliant. Treating normally."); j->dirty_at_shutdown = false; } @@ -3685,10 +3737,7 @@ job_close_shutdown_transaction(job_t j) { if (j->dirty_at_shutdown) { job_log(j, LOG_DEBUG, "Closing shutdown transaction for job."); - if (__sync_sub_and_fetch(&j->shmem->vp_shmem_transaction_cnt, 1) == -1) { - job_log(j, LOG_DEBUG, "Job is now clean. Killing."); - job_kill(j); - } + (void)job_assumes_zero(j, proc_set_dirty(j->p, false)); j->dirty_at_shutdown = false; } } @@ -3699,15 +3748,15 @@ job_log_children_without_exec(job_t j) pid_t *pids = NULL; size_t len = sizeof(pid_t) * get_kern_max_proc(); int i = 0, kp_cnt = 0; - - if (!do_apple_internal_logging || j->anonymous || j->per_user) { + + if (!launchd_apple_internal || j->anonymous || j->per_user) { return; } if (!job_assumes(j, (pids = malloc(len)) != NULL)) { return; } - if (!job_assumes(j, (kp_cnt = proc_listchildpids(j->p, pids, len)) != -1)) { + if (job_assumes_zero_p(j, (kp_cnt = proc_listchildpids(j->p, pids, len))) == -1) { goto out; } @@ -3715,7 +3764,7 @@ job_log_children_without_exec(job_t j) struct proc_bsdshortinfo proc; if (proc_pidinfo(pids[i], PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) { if (errno != ESRCH) { - job_assumes(j, errno == 0); + (void)job_assumes_zero(j, errno); } continue; } @@ -3739,7 +3788,7 @@ job_cleanup_after_tracer(job_t j) struct kevent kev; EV_SET(&kev, j->p, 0, 0, NOTE_EXIT, 0, 0); - /* Fake a kevent to keep our logic consistent. */ + // Fake a kevent to keep our logic consistent. job_callback_proc(j, &kev); /* Normally, after getting a EVFILT_PROC event, we do garbage collection @@ -3747,7 +3796,7 @@ job_cleanup_after_tracer(job_t j) * collection at the beginning of the next run loop cycle (after we're done * draining the current queue of kevents). */ - (void)job_assumes(j, kevent_mod((uintptr_t)&root_jobmgr->reboot_flags, EVFILT_TIMER, EV_ADD | EV_ONESHOT, NOTE_NSECONDS, 1, root_jobmgr) != -1); + (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)&root_jobmgr->reboot_flags, EVFILT_TIMER, EV_ADD | EV_ONESHOT, NOTE_NSECONDS, 1, root_jobmgr)); } } @@ -3756,38 +3805,42 @@ job_callback_proc(job_t j, struct kevent *kev) { bool program_changed = false; int fflags = kev->fflags; - + job_log(j, LOG_DEBUG, "EVFILT_PROC event for job."); log_kevent_struct(LOG_DEBUG, kev, 0); - + if (fflags & NOTE_EXIT) { if (j->p == (pid_t)kev->ident && !j->anonymous) { - /* Note that the third argument to proc_pidinfo() is a magic argument for - * PROC_PIDT_SHORTBSDINFO. Specifically, passing 1 means "don't fail on a zombie - * PID". + /* Note that the third argument to proc_pidinfo() is a magic + * argument for PROC_PIDT_SHORTBSDINFO. Specifically, passing 1 + * means "don't fail on a zombie PID". */ struct proc_bsdshortinfo proc; if (job_assumes(j, proc_pidinfo(j->p, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) > 0)) { if (!job_assumes(j, (pid_t)proc.pbsi_ppid == getpid())) { - /* Someone has attached to the process with ptrace(). There's a race here. - * If we determine that we are not the parent process and then fail to attach - * a kevent to the parent PID (who is probably using ptrace()), we can take that as an - * indication that the parent exited between sysctl(3) and kevent_mod(). The - * reparenting of the PID should be atomic to us, so in that case, we reap the - * job as normal. + /* Someone has attached to the process with ptrace(). + * There's a race here. If we determine that we are not the + * parent process and then fail to attach a kevent to the + * parent PID (who is probably using ptrace()), we can take + * that as an indication that the parent exited between + * sysctl(3) and kevent_mod(). The reparenting of the PID + * should be atomic to us, so in that case, we reap the job + * as normal. * - * Otherwise, we wait for the death of the parent tracer and then reap, just as we - * would if a job died while we were sampling it at shutdown. + * Otherwise, we wait for the death of the parent tracer and + * then reap, just as we would if a job died while we were + * sampling it at shutdown. * - * Note that we foolishly assume that in the process *tree* a node cannot be its - * own parent. Apparently, that is not correct. If this is the case, we forsake - * the process to its own devices. Let it reap itself. + * Note that we foolishly assume that in the process *tree* + * a node cannot be its own parent. Apparently, that is not + * correct. If this is the case, we forsake the process to + * its own devices. Let it reap itself. */ if (!job_assumes(j, proc.pbsi_ppid != kev->ident)) { job_log(j, LOG_WARNING, "Job is its own parent and has (somehow) exited. Leaving it to waste away."); return; } - if (job_assumes(j, kevent_mod(proc.pbsi_ppid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, j) != -1)) { + if (job_assumes_zero_p(j, kevent_mod(proc.pbsi_ppid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, j)) != -1) { j->tracing_pid = proc.pbsi_ppid; j->reap_after_trace = true; return; @@ -3797,17 +3850,17 @@ job_callback_proc(job_t j, struct kevent *kev) } else if (!j->anonymous) { if (j->tracing_pid == (pid_t)kev->ident) { job_cleanup_after_tracer(j); - + return; } else if (j->tracing_pid && !j->reap_after_trace) { - /* The job exited before our sample completed. */ + // The job exited before our sample completed. job_log(j, LOG_DEBUG | LOG_CONSOLE, "Job has exited. Will reap after tracing PID %i exits.", j->tracing_pid); j->reap_after_trace = true; return; } } } - + if (fflags & NOTE_EXEC) { program_changed = true; @@ -3819,28 +3872,26 @@ job_callback_proc(job_t j, struct kevent *kev) snprintf(newlabel, sizeof(newlabel), "%p.anonymous.%s", j, proc.pbsi_comm); job_log(j, LOG_INFO, "Program changed. Updating the label to: %s", newlabel); - j->lastlookup = NULL; - j->lastlookup_gennum = 0; LIST_REMOVE(j, label_hash_sle); strcpy((char *)j->label, newlabel); - + jobmgr_t where2put = root_jobmgr; if (j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN) { where2put = j->mgr; } LIST_INSERT_HEAD(&where2put->label_hash[hash_label(j->label)], j, label_hash_sle); } else if (errno != ESRCH) { - job_assumes(j, errno == 0); + (void)job_assumes_zero(j, errno); } } else { if (j->spawn_reply_port) { errno = job_mig_spawn2_reply(j->spawn_reply_port, BOOTSTRAP_SUCCESS, j->p, j->exit_status_port); if (errno) { if (errno != MACH_SEND_INVALID_DEST) { - (void)job_assumes(j, errno == KERN_SUCCESS); + (void)job_assumes_zero(j, errno); } - (void)job_assumes(j, launchd_mport_close_recv(j->exit_status_port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_close_recv(j->exit_status_port)); } j->spawn_reply_port = MACH_PORT_NULL; @@ -3868,7 +3919,7 @@ job_callback_proc(job_t j, struct kevent *kev) job_remove(j); j = NULL; } else { - j = job_dispatch(j, false); + (void)job_dispatch(j, false); } } } @@ -3900,9 +3951,9 @@ 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; - if (g_trap_sigkill_bugs) { + 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(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER) == KERN_SUCCESS); + (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 @@ -3922,9 +3973,9 @@ job_callback_timer(job_t j, void *ident) * * See . */ - kevent_mod((uintptr_t)j->p, EVFILT_PROC, EV_DELETE, 0, 0, NULL); + (void)kevent_mod((uintptr_t)j->p, EVFILT_PROC, EV_DELETE, 0, 0, NULL); if (j->tracing_pid) { - kevent_mod((uintptr_t)j->tracing_pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL); + (void)kevent_mod((uintptr_t)j->tracing_pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL); } struct kevent bogus_exit; @@ -3933,23 +3984,21 @@ job_callback_timer(job_t j, void *ident) } else { if (unlikely(j->debug_before_kill)) { job_log(j, LOG_NOTICE, "Exit timeout elapsed. Entering the kernel debugger"); - (void)job_assumes(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER) == KERN_SUCCESS); + (void)job_assumes_zero(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER)); } job_log(j, LOG_WARNING | LOG_CONSOLE, "Exit timeout elapsed (%u seconds). Killing", j->exit_timeout); job_kill(j); } } else { - (void)job_assumes(j, false); + job_log(j, LOG_ERR, "Unrecognized job timer callback: %p", ident); } } void job_callback_read(job_t j, int ident) { - if (ident == j->log_redirect_fd) { - job_log_stdouterr(j); - } else if (ident == j->stdin_fd) { + if (ident == j->stdin_fd) { job_dispatch(j, true); } else { socketgroup_callback(j); @@ -3976,7 +4025,12 @@ void jobmgr_callback(void *obj, struct kevent *kev) { jobmgr_t jm = obj; - job_t ji; + +#if TARGET_OS_EMBEDDED + int flag2check = VQ_MOUNT; +#else + int flag2check = VQ_UPDATE; +#endif switch (kev->filter) { case EVFILT_PROC: @@ -3991,32 +4045,27 @@ jobmgr_callback(void *obj, struct kevent *kev) case SIGUSR1: return calendarinterval_callback(); case SIGUSR2: - fake_shutdown_in_progress = true; - runtime_setlogmask(LOG_UPTO(LOG_DEBUG)); - - runtime_closelog(); /* HACK -- force 'start' time to be set */ - - if (pid1_magic) { - int64_t now = runtime_get_wall_time(); - - jobmgr_log(jm, LOG_NOTICE, "Anticipatory shutdown began at: %lld.%06llu", now / USEC_PER_SEC, now % USEC_PER_SEC); - - LIST_FOREACH(ji, &root_jobmgr->jobs, sle) { - if (ji->per_user && ji->p) { - (void)job_assumes(ji, runtime_kill(ji->p, SIGUSR2) != -1); - } - } - } else { - jobmgr_log(jm, LOG_NOTICE, "Anticipatory per-user launchd shutdown"); - } - - return; + // Turn on all logging. + launchd_log_perf = true; + launchd_log_debug = true; + launchd_log_shutdown = true; + /* Hopefully /var is available by this point. If not, uh, oh well. + * It's just a debugging facility. + */ + return jobmgr_log_perf_statistics(jm); default: - return (void)jobmgr_assumes(jm, false); + jobmgr_log(jm, LOG_ERR, "Unrecognized signal: %lu: %s", kev->ident, strsignal(kev->ident)); } break; case EVFILT_FS: - if (kev->fflags & VQ_MOUNT) { + if (kev->fflags & flag2check) { + if (!launchd_var_available) { + struct stat sb; + if (stat("/var/log", &sb) == 0 && (sb.st_mode & S_IWUSR)) { + launchd_var_available = true; + } + } + } else if (kev->fflags & VQ_MOUNT) { jobmgr_dispatch_all(jm, true); } jobmgr_dispatch_all_semaphores(jm); @@ -4029,9 +4078,9 @@ jobmgr_callback(void *obj, struct kevent *kev) jobmgr_still_alive_with_check(jm); } else if (kev->ident == (uintptr_t)&jm->reboot_flags) { jobmgr_do_garbage_collection(jm); - } else if (kev->ident == (uintptr_t)&g_runtime_busy_time) { + } else if (kev->ident == (uintptr_t)&launchd_runtime_busy_time) { jobmgr_log(jm, LOG_DEBUG, "Idle exit timer fired. Shutting down."); - if (jobmgr_assumes(jm, runtime_busy_cnt == 0)) { + if (jobmgr_assumes_zero(jm, runtime_busy_cnt) == 0) { return launchd_shutdown(); } } @@ -4041,22 +4090,23 @@ jobmgr_callback(void *obj, struct kevent *kev) int _no_hang_fd = open("/dev/autofs_nowait", O_EVTONLY | O_NONBLOCK); if (unlikely(_no_hang_fd != -1)) { jobmgr_log(root_jobmgr, LOG_DEBUG, "/dev/autofs_nowait has appeared!"); - (void)jobmgr_assumes(root_jobmgr, kevent_mod((uintptr_t)s_no_hang_fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL) != -1); - (void)jobmgr_assumes(root_jobmgr, runtime_close(s_no_hang_fd) != -1); + (void)jobmgr_assumes_zero_p(root_jobmgr, kevent_mod((uintptr_t)s_no_hang_fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL)); + (void)jobmgr_assumes_zero_p(root_jobmgr, runtime_close(s_no_hang_fd)); s_no_hang_fd = _fd(_no_hang_fd); } - } else if (pid1_magic && g_console && kev->ident == (uintptr_t)fileno(g_console)) { + } else if (pid1_magic && launchd_console && kev->ident == (uintptr_t)fileno(launchd_console)) { int cfd = -1; - if (launchd_assumes((cfd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) != -1)) { + if (jobmgr_assumes_zero_p(jm, cfd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) != -1) { _fd(cfd); - if (!launchd_assumes((g_console = fdopen(cfd, "w")) != NULL)) { - close(cfd); + if (!(launchd_console = fdopen(cfd, "w"))) { + (void)jobmgr_assumes_zero(jm, errno); + (void)close(cfd); } } } break; default: - return (void)jobmgr_assumes(jm, false); + jobmgr_log(jm, LOG_ERR, "Unrecognized kevent filter: %hd", kev->filter); } } @@ -4072,14 +4122,12 @@ job_callback(void *obj, struct kevent *kev) return job_callback_proc(j, kev); case EVFILT_TIMER: return job_callback_timer(j, (void *) kev->ident); - case EVFILT_VNODE: - return semaphoreitem_callback(j, kev); case EVFILT_READ: return job_callback_read(j, (int) kev->ident); case EVFILT_MACHPORT: return (void)job_dispatch(j, true); default: - return (void)job_assumes(j, false); + job_log(j, LOG_ERR, "Unrecognized job callback filter: %hd", kev->filter); } } @@ -4089,21 +4137,20 @@ job_start(job_t j) uint64_t td; int spair[2]; int execspair[2]; - int oepair[2]; char nbuf[64]; pid_t c; bool sipc = false; u_int proc_fflags = NOTE_EXIT|NOTE_FORK|NOTE_EXEC; - + if (!job_assumes(j, j->mgr != NULL)) { return; } - + if (unlikely(job_active(j))) { job_log(j, LOG_DEBUG, "Already started"); return; } - + /* * 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 @@ -4111,74 +4158,53 @@ 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) { time_t respawn_delta = j->min_run_time - (uint32_t)td; - /* - * We technically should ref-count throttled jobs to prevent idle exit, + /* We technically should ref-count throttled jobs to prevent idle exit, * but we're not directly tracking the 'throttled' state at the moment. */ - int level = LOG_WARNING; - if (!j->did_exec && ((j->fail_cnt - 1) % LAUNCHD_LOG_FAILED_EXEC_FREQ) != 0) { - level = LOG_DEBUG; - } - - job_log(j, level, "Throttling respawn: Will start in %ld seconds", respawn_delta); - (void)job_assumes(j, kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, respawn_delta, j) != -1); + job_log(j, LOG_NOTICE, "Throttling respawn: Will start in %ld seconds", respawn_delta); + (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, respawn_delta, j)); job_ignore(j); return; } - + if (likely(!j->legacy_mach_job)) { - sipc = ((!SLIST_EMPTY(&j->sockets) || !SLIST_EMPTY(&j->machservices)) && !j->deny_job_creation) || j->embedded_special_privileges; + sipc = ((!SLIST_EMPTY(&j->sockets) || !SLIST_EMPTY(&j->machservices)) && !j->deny_job_creation) || j->embedded_god; } if (sipc) { - (void)job_assumes(j, socketpair(AF_UNIX, SOCK_STREAM, 0, spair) != -1); - } - - (void)job_assumes(j, socketpair(AF_UNIX, SOCK_STREAM, 0, execspair) != -1); - - if (likely(!j->legacy_mach_job) && job_assumes(j, pipe(oepair) != -1)) { - j->log_redirect_fd = _fd(oepair[0]); - (void)job_assumes(j, fcntl(j->log_redirect_fd, F_SETFL, O_NONBLOCK) != -1); - (void)job_assumes(j, kevent_mod(j->log_redirect_fd, EVFILT_READ, EV_ADD, 0, 0, j) != -1); + (void)job_assumes_zero_p(j, socketpair(AF_UNIX, SOCK_STREAM, 0, spair)); } - + + (void)job_assumes_zero_p(j, socketpair(AF_UNIX, SOCK_STREAM, 0, execspair)); + switch (c = runtime_fork(j->weird_bootstrap ? j->j_port : j->mgr->jm_port)) { case -1: job_log_error(j, LOG_ERR, "fork() failed, will try again in one second"); - (void)job_assumes(j, kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 1, j) != -1); + (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 1, j)); job_ignore(j); - - (void)job_assumes(j, runtime_close(execspair[0]) == 0); - (void)job_assumes(j, runtime_close(execspair[1]) == 0); + + (void)job_assumes_zero(j, runtime_close(execspair[0])); + (void)job_assumes_zero(j, runtime_close(execspair[1])); if (sipc) { - (void)job_assumes(j, runtime_close(spair[0]) == 0); - (void)job_assumes(j, runtime_close(spair[1]) == 0); - } - if (likely(!j->legacy_mach_job)) { - (void)job_assumes(j, runtime_close(oepair[0]) != -1); - (void)job_assumes(j, runtime_close(oepair[1]) != -1); - j->log_redirect_fd = 0; + (void)job_assumes_zero(j, runtime_close(spair[0])); + (void)job_assumes_zero(j, runtime_close(spair[1])); } break; case 0: if (unlikely(_vproc_post_fork_ping())) { _exit(EXIT_FAILURE); } - if (!j->legacy_mach_job) { - (void)job_assumes(j, dup2(oepair[1], STDOUT_FILENO) != -1); - (void)job_assumes(j, dup2(oepair[1], STDERR_FILENO) != -1); - (void)job_assumes(j, runtime_close(oepair[1]) != -1); - } - (void)job_assumes(j, runtime_close(execspair[0]) == 0); - /* wait for our parent to say they've attached a kevent to us */ + + (void)job_assumes_zero(j, runtime_close(execspair[0])); + // wait for our parent to say they've attached a kevent to us read(_fd(execspair[1]), &c, sizeof(c)); - + if (sipc) { - (void)job_assumes(j, runtime_close(spair[0]) == 0); + (void)job_assumes_zero(j, runtime_close(spair[0])); snprintf(nbuf, sizeof(nbuf), "%d", spair[1]); setenv(LAUNCHD_TRUSTED_FD_ENV, nbuf, 1); } @@ -4186,9 +4212,9 @@ job_start(job_t j) break; default: j->start_time = runtime_get_opaque_time(); - + job_log(j, LOG_DEBUG, "Started as PID: %u", c); - + j->did_exec = false; j->xpcproxy_did_exec = false; j->checkedin = false; @@ -4196,49 +4222,53 @@ job_start(job_t j) j->reaped = false; j->crashed = false; j->stopped = false; + j->workaround9359725 = false; if (j->needs_kickoff) { j->needs_kickoff = false; - + if (SLIST_EMPTY(&j->semaphores)) { j->ondemand = false; } } - + if (j->has_console) { - g_wsp = c; + launchd_wsp = c; } - + + job_log(j, LOG_PERF, "Job started."); runtime_add_ref(); total_children++; LIST_INSERT_HEAD(&j->mgr->active_jobs[ACTIVE_JOB_HASH(c)], j, pid_hash_sle); - - if (likely(!j->legacy_mach_job)) { - (void)job_assumes(j, runtime_close(oepair[1]) != -1); - } j->p = c; j->mgr->normal_active_cnt++; j->fork_fd = _fd(execspair[0]); - (void)job_assumes(j, runtime_close(execspair[1]) == 0); + (void)job_assumes_zero(j, runtime_close(execspair[1])); if (sipc) { - (void)job_assumes(j, runtime_close(spair[1]) == 0); + (void)job_assumes_zero(j, runtime_close(spair[1])); ipc_open(_fd(spair[0]), j); } - if (job_assumes(j, kevent_mod(c, EVFILT_PROC, EV_ADD, proc_fflags, 0, root_jobmgr ? root_jobmgr : j->mgr) != -1)) { + if (kevent_mod(c, EVFILT_PROC, EV_ADD, proc_fflags, 0, root_jobmgr ? root_jobmgr : j->mgr) != -1) { job_ignore(j); } else { + if (errno == ESRCH) { + job_log(j, LOG_ERR, "Child was killed before we could attach a kevent."); + } else { + (void)job_assumes(j, errno == ESRCH); + } job_reap(j); - } - - j->wait4debugger_oneshot = false; - struct envitem *ei = NULL, *et = NULL; - SLIST_FOREACH_SAFE(ei, &j->env, sle, et) { - if (ei->one_shot) { - SLIST_REMOVE(&j->env, ei, envitem, sle); - } + /* If we have reaped this job within this same run loop pass, then + * it will be currently ignored. So if there's a failure to attach a + * kevent, we need to make sure that we watch the job so that we can + * respawn it. + * + * See . + */ + job_watch(j); } - + + j->wait4debugger_oneshot = false; if (likely(!j->stall_before_exec)) { job_uncork_fork(j); } @@ -4259,7 +4289,7 @@ job_start_child(job_t j) size_t binpref_out_cnt = 0; size_t i; - (void)job_assumes(j, posix_spawnattr_init(&spattr) == 0); + (void)job_assumes_zero(j, posix_spawnattr_init(&spattr)); job_setup_attributes(j); @@ -4301,25 +4331,45 @@ job_start_child(job_t j) spflags |= POSIX_SPAWN_START_SUSPENDED; } +#if !TARGET_OS_EMBEDDED if (unlikely(j->disable_aslr)) { spflags |= _POSIX_SPAWN_DISABLE_ASLR; } +#endif spflags |= j->pstype; - (void)job_assumes(j, posix_spawnattr_setflags(&spattr, spflags) == 0); - + (void)job_assumes_zero(j, posix_spawnattr_setflags(&spattr, spflags)); if (unlikely(j->j_binpref_cnt)) { - (void)job_assumes(j, posix_spawnattr_setbinpref_np(&spattr, j->j_binpref_cnt, j->j_binpref, &binpref_out_cnt) == 0); + (void)job_assumes_zero(j, posix_spawnattr_setbinpref_np(&spattr, j->j_binpref_cnt, j->j_binpref, &binpref_out_cnt)); (void)job_assumes(j, binpref_out_cnt == j->j_binpref_cnt); } +#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 + * update occurs before the values below are applied. In this case, the flag + * 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, flags, j->jetsam_priority, j->jetsam_memlimit)); +#endif + + if (!j->app) { + (void)job_assumes_zero(j, posix_spawnattr_setcpumonitor(&spattr, 85, 5 * 60)); + } + #if HAVE_QUARANTINE if (j->quarantine_data) { qtn_proc_t qp; if (job_assumes(j, qp = qtn_proc_alloc())) { - if (job_assumes(j, qtn_proc_init_with_data(qp, j->quarantine_data, j->quarantine_data_sz) == 0)) { - (void)job_assumes(j, qtn_proc_apply_to_self(qp) == 0); + if (job_assumes_zero(j, qtn_proc_init_with_data(qp, j->quarantine_data, j->quarantine_data_sz) == 0)) { + (void)job_assumes_zero(j, qtn_proc_apply_to_self(qp)); } } } @@ -4329,7 +4379,7 @@ job_start_child(job_t j) if (j->seatbelt_profile) { char *seatbelt_err_buf = NULL; - if (!job_assumes(j, sandbox_init(j->seatbelt_profile, j->seatbelt_flags, &seatbelt_err_buf) != -1)) { + if (job_assumes_zero_p(j, sandbox_init(j->seatbelt_profile, j->seatbelt_flags, &seatbelt_err_buf)) == -1) { if (seatbelt_err_buf) { job_log(j, LOG_ERR, "Sandbox failed to init: %s", seatbelt_err_buf); } @@ -4345,14 +4395,6 @@ job_start_child(job_t j) } errno = psf(NULL, file2exec, NULL, &spattr, (char *const *)argv, environ); - if (errno != EBADARCH) { - int level = LOG_ERR; - if ((j->fail_cnt++ % LAUNCHD_LOG_FAILED_EXEC_FREQ) != 0) { - level = LOG_DEBUG; - } - job_log_error(j, level, "posix_spawn(\"%s\", ...)", file2exec); - errno = EXIT_FAILURE; - } #if HAVE_SANDBOX out_bad: @@ -4414,8 +4456,8 @@ job_log_pids_with_weird_uids(job_t j) pid_t *pids = NULL; uid_t u = j->mach_uid; int i = 0, kp_cnt = 0; - - if (!do_apple_internal_logging) { + + if (!launchd_apple_internal) { return; } @@ -4439,7 +4481,7 @@ job_log_pids_with_weird_uids(job_t j) * Note that proc_list*() APIs return the number of PIDs given back, not the number * of bytes written to the buffer. */ - if (!job_assumes(j, (kp_cnt = proc_listallpids(pids, len)) != -1)) { + if (job_assumes_zero_p(j, (kp_cnt = proc_listallpids(pids, len))) == -1) { goto out; } @@ -4450,11 +4492,11 @@ job_log_pids_with_weird_uids(job_t j) */ if (proc_pidinfo(pids[i], PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) { if (errno != ESRCH) { - job_assumes(j, errno == 0); + (void)job_assumes_zero(j, errno); } continue; } - + uid_t i_euid = proc.pbsi_uid; uid_t i_uid = proc.pbsi_ruid; uid_t i_svuid = proc.pbsi_svuid; @@ -4466,10 +4508,10 @@ job_log_pids_with_weird_uids(job_t j) job_log(j, LOG_ERR, "PID %u \"%s\" has no account to back it! Real/effective/saved UIDs: %u/%u/%u", i_pid, proc.pbsi_comm, i_uid, i_euid, i_svuid); -/* Temporarily disabled due to 5423935 and 4946119. */ +// Temporarily disabled due to 5423935 and 4946119. #if 0 - /* Ask the accountless process to exit. */ - (void)job_assumes(j, runtime_kill(i_pid, SIGTERM) != -1); + // Ask the accountless process to exit. + (void)job_assumes_zero_p(j, kill2(i_pid, SIGTERM)); #endif } @@ -4480,35 +4522,35 @@ out: static struct passwd * job_getpwnam(job_t j, const char *name) { - /* - * methodology for system daemons - * - * first lookup user record without any opendirectoryd interaction, - * we don't know what interprocess dependencies might be in flight. - * if that fails, we re-enable opendirectoryd interaction and - * re-issue the lookup. We have to disable the libinfo L1 cache - * otherwise libinfo will return the negative cache entry on the retry - */ - + /* + * methodology for system daemons + * + * first lookup user record without any opendirectoryd interaction, + * we don't know what interprocess dependencies might be in flight. + * if that fails, we re-enable opendirectoryd interaction and + * re-issue the lookup. We have to disable the libinfo L1 cache + * otherwise libinfo will return the negative cache entry on the retry + */ #if !TARGET_OS_EMBEDDED - struct passwd *pw = NULL; - - if (pid1_magic && j->mgr == root_jobmgr) { - si_search_module_set_flags("ds", 1 /* SEARCH_MODULE_FLAG_DISABLED */); - gL1CacheEnabled = false; - - pw = getpwnam(name); + struct passwd *pw = NULL; - si_search_module_set_flags("ds", 0); - } - - if (pw == NULL) { - pw = getpwnam(name); - } - - return pw; + if (pid1_magic && j->mgr == root_jobmgr) { + // 1 == SEARCH_MODULE_FLAG_DISABLED + si_search_module_set_flags("ds", 1); + gL1CacheEnabled = false; + + pw = getpwnam(name); + si_search_module_set_flags("ds", 0); + } + + if (pw == NULL) { + pw = getpwnam(name); + } + + return pw; #else - return getpwnam(name); +#pragma unused (j) + return getpwnam(name); #endif } @@ -4519,7 +4561,7 @@ job_getgrnam(job_t j, const char *name) struct group *gr = NULL; if (pid1_magic && j->mgr == root_jobmgr) { - si_search_module_set_flags("ds", 1 /* SEARCH_MODULE_FLAG_DISABLED */); + si_search_module_set_flags("ds", 1); gL1CacheEnabled = false; gr = getgrnam(name); @@ -4541,7 +4583,7 @@ job_getgrnam(job_t j, const char *name) void job_postfork_test_user(job_t j) { - /* This function is all about 5201578 */ + // This function is all about 5201578 const char *home_env_var = getenv("HOME"); const char *user_env_var = getenv("USER"); @@ -4601,7 +4643,7 @@ job_postfork_test_user(job_t j) return; out_bad: #if 0 - (void)job_assumes(j, runtime_kill(getppid(), SIGTERM) != -1); + (void)job_assumes_zero_p(j, kill2(getppid(), SIGTERM)); _exit(EXIT_FAILURE); #else job_log(j, LOG_WARNING, "In a future build of the OS, this error will be fatal."); @@ -4635,13 +4677,13 @@ job_postfork_become_user(job_t j) if (j->username) { if ((pwe = job_getpwnam(j, j->username)) == NULL) { job_log(j, LOG_ERR, "getpwnam(\"%s\") failed", j->username); - _exit(EXIT_FAILURE); + _exit(ESRCH); } } else if (j->mach_uid) { if ((pwe = getpwuid(j->mach_uid)) == NULL) { job_log(j, LOG_ERR, "getpwuid(\"%u\") failed", j->mach_uid); job_log_pids_with_weird_uids(j); - _exit(EXIT_FAILURE); + _exit(ESRCH); } } else { return; @@ -4679,17 +4721,17 @@ job_postfork_become_user(job_t j) if (unlikely((gre = job_getgrnam(j, j->groupname)) == NULL)) { job_log(j, LOG_ERR, "getgrnam(\"%s\") failed", j->groupname); - _exit(EXIT_FAILURE); + _exit(ESRCH); } desired_gid = gre->gr_gid; } - if (!job_assumes(j, setlogin(loginname) != -1)) { + if (job_assumes_zero_p(j, setlogin(loginname)) == -1) { _exit(EXIT_FAILURE); } - if (!job_assumes(j, setgid(desired_gid) != -1)) { + if (job_assumes_zero_p(j, setgid(desired_gid)) == -1) { _exit(EXIT_FAILURE); } @@ -4699,27 +4741,27 @@ job_postfork_become_user(job_t j) */ if (likely(!j->no_init_groups)) { - #if 1 - if (!job_assumes(j, initgroups(loginname, desired_gid) != -1)) { +#if 1 + if (job_assumes_zero_p(j, initgroups(loginname, desired_gid)) == -1) { _exit(EXIT_FAILURE); } - #else +#else /* Do our own little initgroups(). We do this to guarantee that we're * always opted into dynamic group resolution in the kernel. initgroups(3) * does not make this guarantee. */ int groups[NGROUPS], ngroups; - - /* A failure here isn't fatal, and we'll still get data we can use. */ - (void)job_assumes(j, getgrouplist(j->username, desired_gid, groups, &ngroups) != -1); - - if (!job_assumes(j, syscall(SYS_initgroups, ngroups, groups, desired_uid) != -1)) { + + // A failure here isn't fatal, and we'll still get data we can use. + (void)job_assumes_zero_p(j, getgrouplist(j->username, desired_gid, groups, &ngroups)); + + if (job_assumes_zero_p(j, syscall(SYS_initgroups, ngroups, groups, desired_uid)) == -1) { _exit(EXIT_FAILURE); } - #endif +#endif } - if (!job_assumes(j, setuid(desired_uid) != -1)) { + if (job_assumes_zero_p(j, setuid(desired_uid)) == -1) { _exit(EXIT_FAILURE); } @@ -4742,13 +4784,13 @@ job_setup_attributes(job_t j) struct envitem *ei; if (unlikely(j->setnice)) { - (void)job_assumes(j, setpriority(PRIO_PROCESS, 0, j->nice) != -1); + (void)job_assumes_zero_p(j, setpriority(PRIO_PROCESS, 0, j->nice)); } SLIST_FOREACH(li, &j->limits, sle) { struct rlimit rl; - if (!job_assumes(j, getrlimit(li->which, &rl) != -1)) { + if (job_assumes_zero_p(j, getrlimit(li->which, &rl) == -1)) { continue; } @@ -4769,17 +4811,23 @@ job_setup_attributes(job_t j) } if (unlikely(j->low_pri_io)) { - (void)job_assumes(j, setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE) != -1); + (void)job_assumes_zero_p(j, setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE)); } if (unlikely(j->rootdir)) { - (void)job_assumes(j, chroot(j->rootdir) != -1); - (void)job_assumes(j, chdir(".") != -1); + (void)job_assumes_zero_p(j, chroot(j->rootdir)); + (void)job_assumes_zero_p(j, chdir(".")); } job_postfork_become_user(j); if (unlikely(j->workingdir)) { - (void)job_assumes(j, chdir(j->workingdir) != -1); + if (chdir(j->workingdir) == -1) { + if (errno == ENOENT || errno == ENOTDIR) { + job_log(j, LOG_ERR, "Job specified non-existent working directory: %s", j->workingdir); + } else { + (void)job_assumes_zero(j, errno); + } + } } if (unlikely(j->setmask)) { @@ -4787,7 +4835,7 @@ job_setup_attributes(job_t j) } if (j->stdin_fd) { - (void)job_assumes(j, dup2(j->stdin_fd, STDIN_FILENO) != -1); + (void)job_assumes_zero_p(j, dup2(j->stdin_fd, STDIN_FILENO)); } else { job_setup_fd(j, STDIN_FILENO, j->stdinpath, O_RDONLY|O_CREAT); } @@ -4800,13 +4848,9 @@ job_setup_attributes(job_t j) setenv(ei->key, ei->value, 1); } - if (do_apple_internal_logging) { - setenv(LAUNCHD_DO_APPLE_INTERNAL_LOGGING, "true", 1); - } - #if !TARGET_OS_EMBEDDED if (j->jetsam_properties) { - (void)job_assumes(j, proc_setpcontrol(PROC_SETPC_TERMINATE) == 0); + (void)job_assumes_zero(j, proc_setpcontrol(PROC_SETPC_TERMINATE)); } #endif @@ -4815,7 +4859,7 @@ job_setup_attributes(job_t j) struct sched_param params; bzero(¶ms, sizeof(params)); params.sched_priority = j->main_thread_priority; - (void)job_assumes(j, pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶ms) != -1); + (void)job_assumes_zero_p(j, pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶ms)); } #endif @@ -4825,9 +4869,9 @@ job_setup_attributes(job_t j) * setuid children. We'll settle for process-groups. */ if (getppid() != 1) { - (void)job_assumes(j, setpgid(0, 0) != -1); + (void)job_assumes_zero_p(j, setpgid(0, 0)); } else { - (void)job_assumes(j, setsid() != -1); + (void)job_assumes_zero_p(j, setsid()); } } @@ -4845,30 +4889,8 @@ job_setup_fd(job_t j, int target_fd, const char *path, int flags) return; } - (void)job_assumes(j, dup2(fd, target_fd) != -1); - (void)job_assumes(j, runtime_close(fd) == 0); -} - -int -dir_has_files(job_t j, const char *path) -{ - DIR *dd = opendir(path); - struct dirent *de; - bool r = 0; - - if (unlikely(!dd)) { - return -1; - } - - while ((de = readdir(dd))) { - if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) { - r = 1; - break; - } - } - - (void)job_assumes(j, closedir(dd) == 0); - return r; + (void)job_assumes_zero_p(j, dup2(fd, target_fd)); + (void)job_assumes_zero(j, runtime_close(fd)); } void @@ -4901,7 +4923,7 @@ calendarinterval_setalarm(job_t j, struct calendarinterval *ci) } if (ci_iter == NULL) { - /* ci must want to fire after every other timer, or there are no timers */ + // ci must want to fire after every other timer, or there are no timers if (LIST_EMPTY(&sorted_calendar_events)) { LIST_INSERT_HEAD(&sorted_calendar_events, ci, global_sle); @@ -4912,7 +4934,7 @@ calendarinterval_setalarm(job_t j, struct calendarinterval *ci) head_later = LIST_FIRST(&sorted_calendar_events)->when_next; - if (job_assumes(j, kevent_mod((uintptr_t)&sorted_calendar_events, EVFILT_TIMER, EV_ADD, NOTE_ABSOLUTE|NOTE_SECONDS, head_later, root_jobmgr) != -1)) { + if (job_assumes_zero_p(j, kevent_mod((uintptr_t)&sorted_calendar_events, EVFILT_TIMER, EV_ADD, NOTE_ABSOLUTE|NOTE_SECONDS, head_later, root_jobmgr)) != -1) { char time_string[100]; size_t time_string_len; @@ -4927,90 +4949,120 @@ calendarinterval_setalarm(job_t j, struct calendarinterval *ci) } } -void -extract_rcsid_substr(const char *i, char *o, size_t osz) +bool +jobmgr_log_bug(aslmsg asl_message __attribute__((unused)), void *ctx, const char *message) { - char *rcs_rev_tmp = strchr(i, ' '); + jobmgr_t jm = ctx; + jobmgr_log(jm, LOG_ERR, "%s", message); - if (!rcs_rev_tmp) { - strlcpy(o, i, osz); - } else { - strlcpy(o, rcs_rev_tmp + 1, osz); - rcs_rev_tmp = strchr(o, ' '); - if (rcs_rev_tmp) { - *rcs_rev_tmp = '\0'; - } - } + return true; } -void -jobmgr_log_bug(jobmgr_t jm, unsigned int line) +bool +job_log_bug(aslmsg asl_message __attribute__((unused)), void *ctx, const char *message) { - static const char *file; - int saved_errno = errno; - char buf[100]; - - runtime_ktrace1(RTKT_LAUNCHD_BUG); - - extract_rcsid_substr(__rcs_file_version__, buf, sizeof(buf)); - - if (!file) { - file = strrchr(__FILE__, '/'); - if (!file) { - file = __FILE__; - } else { - file += 1; - } - } + job_t j = ctx; + job_log(j, LOG_ERR, "%s", message); - /* the only time 'jm' should not be set is if setting up the first bootstrap fails for some reason */ - if (likely(jm)) { - jobmgr_log(jm, LOG_NOTICE, "Bug: %s:%u (%s):%u", file, line, buf, saved_errno); - } else { - runtime_syslog(LOG_NOTICE, "Bug: %s:%u (%s):%u", file, line, buf, saved_errno); - } + return true; } void -job_log_bug(job_t j, unsigned int line) +job_log_perf_statistics(job_t j) { - static const char *file; - int saved_errno = errno; - char buf[100]; - - runtime_ktrace1(RTKT_LAUNCHD_BUG); + if (j->anonymous) { + return; + } + if (!launchd_log_perf) { + return; + } - extract_rcsid_substr(__rcs_file_version__, buf, sizeof(buf)); + 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); + } - if (!file) { - file = strrchr(__FILE__, '/'); - if (!file) { - file = __FILE__; + 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 { - file += 1; + job_log(j, LOG_PERF, "proc_pidinfo(%d): %d: %s", j->p, errno, strerror(errno)); } } - if (likely(j)) { - job_log(j, LOG_NOTICE, "Bug: %s:%u (%s):%u", file, line, buf, saved_errno); - } else { - runtime_syslog(LOG_NOTICE, "Bug: %s:%u (%s):%u", file, line, buf, saved_errno); + if (!j->ondemand) { + job_log(j, LOG_PERF, "Job is configured to always run."); + } + + 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); + } } } 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.NULL"; - const char *mgr2use = j ? j->mgr->name : "NULL"; - struct runtime_syslog_attr attr = { g_my_label, label2use, mgr2use, pri, getuid(), getpid(), j ? j->p : 0 }; + 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; - /* - * Hack: If bootstrap_port is set, we must be on the child side of a - * fork(), but before the exec*(). Let's route the log message back to + struct launchd_syslog_attr attr = { + .from_name = launchd_label, + .about_name = label2use, + .session_name = mgr2use, + .priority = pri, + .from_uid = getuid(), + .from_pid = getpid(), + .about_pid = j ? j->p : 0, + }; + + /* Hack: If bootstrap_port is set, we must be on the child side of a + * fork(2), but before the exec*(3). Let's route the log message back to * launchd proper. */ if (bootstrap_port) { @@ -5022,9 +5074,9 @@ job_logv(job_t j, int pri, int err, const char *msg, va_list ap) if (err) { #if !TARGET_OS_EMBEDDED - snprintf(newmsg, newmsgsz, "%s: %s", msg, strerror(err)); + snprintf(newmsg, newmsgsz, "%s: %d: %s", msg, err, strerror(err)); #else - snprintf(newmsg, newmsgsz, "(%s) %s: %s", label2use, msg, strerror(err)); + snprintf(newmsg, newmsgsz, "(%s) %s: %d: %s", label2use, msg, err, strerror(err)); #endif } else { #if !TARGET_OS_EMBEDDED @@ -5038,7 +5090,7 @@ job_logv(job_t j, int pri, int err, const char *msg, va_list ap) oldmask = setlogmask(LOG_UPTO(LOG_DEBUG)); } - runtime_vsyslog(&attr, newmsg, ap); + launchd_vsyslog(&attr, newmsg, ap); if (j && unlikely(j->debug)) { setlogmask(oldmask); @@ -5077,6 +5129,32 @@ jobmgr_log_error(jobmgr_t jm, int pri, const char *msg, ...) } #endif +void +jobmgr_log_perf_statistics(jobmgr_t jm) +{ + jobmgr_t jmi = NULL; + SLIST_FOREACH(jmi, &jm->submgrs, sle) { + jobmgr_log_perf_statistics(jmi); + } + + if (jm->xpc_singleton) { + jobmgr_log(jm, LOG_PERF, "XPC Singleton Domain: %s", jm->shortdesc); + } else if (jm->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN) { + jobmgr_log(jm, LOG_PERF, "XPC Private Domain: %s", jm->owner); + } else if (jm->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET) { + jobmgr_log(jm, LOG_PERF, "Created via bootstrap_subset()"); + } + + jobmgr_log(jm, LOG_PERF, "Jobs in job manager:"); + + job_t ji = NULL; + LIST_FOREACH(ji, &jm->jobs, sle) { + job_log_perf_statistics(ji); + } + + jobmgr_log(jm, LOG_PERF, "End of job list."); +} + void jobmgr_log(jobmgr_t jm, int pri, const char *msg, ...) { @@ -5090,6 +5168,10 @@ jobmgr_log(jobmgr_t jm, int pri, const char *msg, ...) void jobmgr_logv(jobmgr_t jm, int pri, int err, const char *msg, va_list ap) { + if (!jm) { + jm = root_jobmgr; + } + char *newmsg; char *newname; size_t i, o, jmname_len = strlen(jm->name), newmsgsz; @@ -5116,181 +5198,18 @@ jobmgr_logv(jobmgr_t jm, int pri, int err, const char *msg, va_list ap) if (jm->parentmgr) { jobmgr_logv(jm->parentmgr, pri, 0, newmsg, ap); } else { - struct runtime_syslog_attr attr = { g_my_label, g_my_label, jm->name, pri, getuid(), getpid(), getpid() }; - - runtime_vsyslog(&attr, newmsg, ap); - } -} - -void -semaphoreitem_ignore(job_t j, struct semaphoreitem *si) -{ - if (si->fd != -1) { - job_log(j, LOG_DEBUG, "Ignoring Vnode: %d", si->fd); - (void)job_assumes(j, kevent_mod(si->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL) != -1); - } -} - -void -semaphoreitem_watch(job_t j, struct semaphoreitem *si) -{ - char *parentdir, tmp_path[PATH_MAX]; - int saved_errno = 0; - int fflags = NOTE_DELETE|NOTE_RENAME; - - switch (si->why) { - case DIR_NOT_EMPTY: - case PATH_CHANGES: - fflags |= NOTE_ATTRIB|NOTE_LINK; - /* fall through */ - case PATH_EXISTS: - fflags |= NOTE_REVOKE|NOTE_EXTEND|NOTE_WRITE; - /* fall through */ - case PATH_MISSING: - break; - default: - return; - } - - /* dirname() may modify tmp_path */ - strlcpy(tmp_path, si->what, sizeof(tmp_path)); - - if (!job_assumes(j, (parentdir = dirname(tmp_path)))) { - return; - } - - /* See 5321044 for why we do the do-while loop and 5415523 for why ENOENT is checked */ - do { - if (si->fd == -1) { - struct stat sb; - if (stat(si->what, &sb) == 0) { - /* If we're watching a character or block device, only watch the parent directory. - * See rdar://problem/6489900 for the gory details. Basically, holding an open file - * descriptor to a devnode could end up (a) blocking us on open(2) until someone else - * open(2)s the file (like a character device that waits for a carrier signal) or - * (b) preventing other processes from obtaining an exclusive lock on the file, even - * though we're opening it with O_EVTONLY. - * - * The main point of contention is that O_EVTONLY doesn't actually mean "event only". - * It means "Don't prevent unmounts of this descriptor's volume". We work around this - * for dev nodes by only watching the parent directory and stat(2)ing our desired file - * each time the parent changes to see if it appeared or disappeared. - */ - if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) { - si->fd = _fd(open(si->what, O_EVTONLY | O_NOCTTY | O_NONBLOCK)); - } - } - - if (si->fd == -1) { - si->watching_parent = job_assumes(j, (si->fd = _fd(open(parentdir, O_EVTONLY | O_NOCTTY | O_NONBLOCK))) != -1); - } else { - si->watching_parent = false; - } - } - - if (si->fd == -1) { - return job_log_error(j, LOG_ERR, "Path monitoring failed on \"%s\"", si->what); - } - - job_log(j, LOG_DEBUG, "Watching %svnode (%s): %d", si->watching_parent ? "parent ": "", si->what, si->fd); - - if (kevent_mod(si->fd, EVFILT_VNODE, EV_ADD, fflags, 0, j) == -1) { - saved_errno = errno; - /* - * The FD can be revoked between the open() and kevent(). - * This is similar to the inability for kevents to be - * attached to short lived zombie processes after fork() - * but before kevent(). - */ - (void)job_assumes(j, runtime_close(si->fd) == 0); - si->fd = -1; - } - } while (unlikely((si->fd == -1) && (saved_errno == ENOENT))); - - if (saved_errno == ENOTSUP) { - /* - * 3524219 NFS needs kqueue support - * 4124079 VFS needs generic kqueue support - * 5226811 EVFILT: Launchd EVFILT_VNODE doesn't work on /dev - */ - job_log(j, LOG_DEBUG, "Falling back to polling for path: %s", si->what); - - if (!j->poll_for_vfs_changes) { - j->poll_for_vfs_changes = true; - (void)job_assumes(j, kevent_mod((uintptr_t)&j->semaphores, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 3, j) != -1); - } - } -} - -void -semaphoreitem_callback(job_t j, struct kevent *kev) -{ - char invalidation_reason[100] = ""; - struct semaphoreitem *si; - - SLIST_FOREACH(si, &j->semaphores, sle) { - switch (si->why) { - case PATH_CHANGES: - case PATH_EXISTS: - case PATH_MISSING: - case DIR_NOT_EMPTY: - job_log(j, LOG_DEBUG, "P%s changed (%u): %s", si->watching_parent ? "arent path" : "ath", si->why, si->what); - break; - default: - continue; - } - - if (si->fd == (int)kev->ident) { - break; - } - } - - if (!job_assumes(j, si != NULL)) { - return; - } - - if (NOTE_DELETE & kev->fflags) { - strcat(invalidation_reason, "deleted"); - } - - if (NOTE_RENAME & kev->fflags) { - if (invalidation_reason[0]) { - strcat(invalidation_reason, "/renamed"); - } else { - strcat(invalidation_reason, "renamed"); - } - } - - if (NOTE_REVOKE & kev->fflags) { - if (invalidation_reason[0]) { - strcat(invalidation_reason, "/revoked"); - } else { - strcat(invalidation_reason, "revoked"); - } - } - - if (invalidation_reason[0]) { - job_log(j, LOG_DEBUG, "Path %s: %s", invalidation_reason, si->what); - (void)job_assumes(j, runtime_close(si->fd) == 0); - si->fd = -1; /* this will get fixed in semaphoreitem_watch() */ - } + struct launchd_syslog_attr attr = { + .from_name = launchd_label, + .about_name = launchd_label, + .session_name = jm->name, + .priority = pri, + .from_uid = getuid(), + .from_pid = getpid(), + .about_pid = getpid(), + }; - if (!si->watching_parent) { - if (si->why == PATH_CHANGES) { - j->start_pending = true; - } else { - semaphoreitem_watch(j, si); - } - } else { /* Something happened to the parent directory. See if our target file appeared. */ - if (!invalidation_reason[0]) { - (void)job_assumes(j, runtime_close(si->fd) == 0); - si->fd = -1; /* this will get fixed in semaphoreitem_watch() */ - semaphoreitem_watch(j, si); - } - /* Need to think about what should happen if the parent directory goes invalid. */ + launchd_vsyslog(&attr, newmsg, ap); } - - job_dispatch(j, false); } struct cal_dict_walk { @@ -5307,7 +5226,7 @@ calendarinterval_new_from_obj_dict_walk(launch_data_t obj, const char *key, void int64_t val; if (unlikely(LAUNCH_DATA_INTEGER != launch_data_get_type(obj))) { - /* hack to let caller know something went wrong */ + // hack to let caller know something went wrong tmptm->tm_sec = -1; return; } @@ -5350,7 +5269,7 @@ calendarinterval_new_from_obj_dict_walk(launch_data_t obj, const char *key, void tmptm->tm_sec = -1; } else { tmptm->tm_mon = (typeof(tmptm->tm_mon)) val; - tmptm->tm_mon -= 1; /* 4798263 cron compatibility */ + tmptm->tm_mon -= 1; // 4798263 cron compatibility } } } @@ -5399,7 +5318,7 @@ calendarinterval_new(job_t j, struct tm *w) ci->job = j; SLIST_INSERT_HEAD(&j->cal_intervals, ci, sle); - + calendarinterval_setalarm(j, ci); runtime_add_weak_ref(); @@ -5425,7 +5344,7 @@ calendarinterval_sanity_check(void) time_t now = time(NULL); if (unlikely(ci && (ci->when_next < now))) { - (void)jobmgr_assumes(root_jobmgr, raise(SIGUSR1) != -1); + (void)jobmgr_assumes_zero_p(root_jobmgr, raise(SIGUSR1)); } } @@ -5451,7 +5370,7 @@ calendarinterval_callback(void) } bool -socketgroup_new(job_t j, const char *name, int *fds, size_t fd_cnt, bool junkfds) +socketgroup_new(job_t j, const char *name, int *fds, size_t fd_cnt) { struct socketgroup *sg = calloc(1, sizeof(struct socketgroup) + strlen(name) + 1); @@ -5461,7 +5380,6 @@ socketgroup_new(job_t j, const char *name, int *fds, size_t fd_cnt, bool junkfds sg->fds = calloc(1, fd_cnt * sizeof(int)); sg->fd_cnt = fd_cnt; - sg->junkfds = junkfds; if (!job_assumes(j, sg->fds != NULL)) { free(sg); @@ -5489,14 +5407,14 @@ socketgroup_delete(job_t j, struct socketgroup *sg) struct sockaddr_un *sun = (struct sockaddr_un *)&ss; socklen_t ss_len = sizeof(ss); - /* 5480306 */ - if (job_assumes(j, getsockname(sg->fds[i], (struct sockaddr *)&ss, &ss_len) != -1) + // 5480306 + if (job_assumes_zero(j, getsockname(sg->fds[i], (struct sockaddr *)&ss, &ss_len) != -1) && job_assumes(j, ss_len > 0) && (ss.ss_family == AF_UNIX)) { (void)job_assumes(j, unlink(sun->sun_path) != -1); - /* We might conditionally need to delete a directory here */ + // We might conditionally need to delete a directory here } #endif - (void)job_assumes(j, runtime_close(sg->fds[i]) != -1); + (void)job_assumes_zero_p(j, runtime_close(sg->fds[i])); } SLIST_REMOVE(&j->sockets, sg, socketgroup, sle); @@ -5514,10 +5432,6 @@ socketgroup_kevent_mod(job_t j, struct socketgroup *sg, bool do_add) char buf[10000]; unsigned int i, buf_off = 0; - if (unlikely(sg->junkfds)) { - return; - } - for (i = 0; i < sg->fd_cnt; i++) { EV_SET(&kev[i], sg->fds[i], EVFILT_READ, do_add ? EV_ADD : EV_DELETE, 0, 0, j); buf_off += snprintf(buf + buf_off, sizeof(buf) - buf_off, " %d", sg->fds[i]); @@ -5525,12 +5439,12 @@ socketgroup_kevent_mod(job_t j, struct socketgroup *sg, bool do_add) job_log(j, LOG_DEBUG, "%s Sockets:%s", do_add ? "Watching" : "Ignoring", buf); - (void)job_assumes(j, kevent_bulk_mod(kev, sg->fd_cnt) != -1); + (void)job_assumes_zero_p(j, kevent_bulk_mod(kev, sg->fd_cnt)); for (i = 0; i < sg->fd_cnt; i++) { (void)job_assumes(j, kev[i].flags & EV_ERROR); errno = (typeof(errno)) kev[i].data; - (void)job_assumes(j, kev[i].data == 0); + (void)job_assumes_zero(j, kev[i].data); } } @@ -5553,8 +5467,15 @@ socketgroup_callback(job_t j) } bool -envitem_new(job_t j, const char *k, const char *v, bool global, bool one_shot) +envitem_new(job_t j, const char *k, const char *v, bool global) { + if (global && !launchd_allow_global_dyld_envvars) { + if (strncmp("DYLD_", k, sizeof("DYLD_") - 1) == 0) { + job_log(j, LOG_ERR, "Ignoring global environment variable submitted by job (variable=value): %s=%s", k, v); + return false; + } + } + struct envitem *ei = calloc(1, sizeof(struct envitem) + strlen(k) + 1 + strlen(v) + 1); if (!job_assumes(j, ei != NULL)) { @@ -5564,7 +5485,6 @@ envitem_new(job_t j, const char *k, const char *v, bool global, bool one_shot) strcpy(ei->key_init, k); ei->value = ei->key_init + strlen(k) + 1; strcpy(ei->value, v); - ei->one_shot = one_shot; if (global) { if (SLIST_EMPTY(&j->global_env)) { @@ -5605,28 +5525,12 @@ envitem_setup(launch_data_t obj, const char *key, void *context) } if (strncmp(LAUNCHD_TRUSTED_FD_ENV, key, sizeof(LAUNCHD_TRUSTED_FD_ENV) - 1) != 0) { - envitem_new(j, key, launch_data_get_string(obj), j->importing_global_env, false); + envitem_new(j, key, launch_data_get_string(obj), j->importing_global_env); } else { job_log(j, LOG_DEBUG, "Ignoring reserved environmental variable: %s", key); } } -void -envitem_setup_one_shot(launch_data_t obj, const char *key, void *context) -{ - job_t j = context; - - if (launch_data_get_type(obj) != LAUNCH_DATA_STRING) { - return; - } - - if (strncmp(LAUNCHD_TRUSTED_FD_ENV, key, sizeof(LAUNCHD_TRUSTED_FD_ENV) - 1) != 0) { - envitem_new(j, key, launch_data_get_string(obj), j->importing_global_env, true); - } else { - job_log(j, LOG_DEBUG, "Ignoring reserved environmental variable: %s", key); - } -} - bool limititem_update(job_t j, int w, rlim_t r) { @@ -5730,7 +5634,7 @@ job_useless(job_t j) return true; } else if (j->shutdown_monitor) { return false; - } else if (j->mgr->shutting_down) { + } else if (j->mgr->shutting_down && !j->mgr->parentmgr) { job_log(j, LOG_DEBUG, "Exited while shutdown in progress. Processes remaining: %lu/%lu", total_children, total_anon_children); if (total_children == 0 && !j->anonymous) { job_log(j, LOG_DEBUG | LOG_CONSOLE, "Job was last to exit during shutdown of: %s.", j->mgr->name); @@ -5768,9 +5672,8 @@ job_keepalive(job_t j) mach_port_status_t status; struct semaphoreitem *si; struct machservice *ms; - struct stat sb; bool good_exit = (WIFEXITED(j->last_exit_status) && WEXITSTATUS(j->last_exit_status) == 0); - bool is_not_kextd = (do_apple_internal_logging || (strcmp(j->label, "com.apple.kextd") != 0)); + bool is_not_kextd = (launchd_apple_internal || (strcmp(j->label, "com.apple.kextd") != 0)); if (unlikely(j->mgr->shutting_down)) { return false; @@ -5813,7 +5716,7 @@ job_keepalive(job_t j) return true; } } - + /* TODO: Coalesce external events and semaphore items, since they're basically * the same thing. */ @@ -5823,10 +5726,9 @@ job_keepalive(job_t j) return true; } } - + SLIST_FOREACH(si, &j->semaphores, sle) { bool wanted_state = false; - int qdir_file_cnt; job_t other_j; switch (si->why) { @@ -5871,38 +5773,6 @@ job_keepalive(job_t j) } } break; - case PATH_EXISTS: - wanted_state = true; - case PATH_MISSING: - if ((bool)(stat(si->what, &sb) == 0) == wanted_state) { - job_log(j, LOG_DEBUG, "KeepAlive: The following path %s: %s", wanted_state ? "exists" : "is missing", si->what); - return true; - } else { - if (wanted_state) { /* File is not there but we wish it was. */ - if (si->fd != -1 && !si->watching_parent) { /* Need to be watching the parent now. */ - (void)job_assumes(j, runtime_close(si->fd) == 0); - si->fd = -1; - semaphoreitem_watch(j, si); - } - } else { /* File is there but we wish it wasn't. */ - if (si->fd != -1 && si->watching_parent) { /* Need to watch the file now. */ - (void)job_assumes(j, runtime_close(si->fd) == 0); - si->fd = -1; - semaphoreitem_watch(j, si); - } - } - } - break; - case PATH_CHANGES: - break; - case DIR_NOT_EMPTY: - if (-1 == (qdir_file_cnt = dir_has_files(j, si->what))) { - job_log_error(j, LOG_ERR, "Failed to count the number of files in \"%s\"", si->what); - } else if (qdir_file_cnt > 0) { - job_log(j, LOG_DEBUG, "KeepAlive: Directory is not empty: %s", si->what); - return true; - } - break; } } @@ -5920,26 +5790,19 @@ job_active(job_t j) return "PID is still valid"; } - if (j->mgr->shutting_down && j->log_redirect_fd) { - (void)job_assumes(j, runtime_close(j->log_redirect_fd) != -1); - j->log_redirect_fd = 0; - } - - if (j->log_redirect_fd) { - if (job_assumes(j, j->legacy_LS_job)) { - return "Standard out/error is still valid"; - } else { - (void)job_assumes(j, runtime_close(j->log_redirect_fd) != -1); - j->log_redirect_fd = 0; - } - } - if (j->priv_port_has_senders) { return "Privileged Port still has outstanding senders"; } SLIST_FOREACH(ms, &j->machservices, sle) { - if (ms->recv && machservice_active(ms)) { + /* If we've simulated an exit, we mark the job as non-active, even + * though doing so will leave it in an unsafe state. We do this so that + * shutdown can proceed. See . + * + * For a more sustainable solution, see . + */ + if (!j->workaround9359725 && ms->recv && machservice_active(ms)) { + job_log(j, LOG_INFO, "Mach service is still active: %s", ms->name); return "Mach service is still active"; } } @@ -5951,33 +5814,77 @@ void machservice_watch(job_t j, struct machservice *ms) { if (ms->recv) { - (void)job_assumes(j, runtime_add_mport(ms->port, NULL, 0) == KERN_SUCCESS); + (void)job_assumes_zero(j, runtime_add_mport(ms->port, NULL)); } } void machservice_ignore(job_t j, struct machservice *ms) { - (void)job_assumes(j, runtime_remove_mport(ms->port) == KERN_SUCCESS); + /* We only add ports whose receive rights we control into the port set, so + * don't attempt to remove te service from the port set if we didn't put it + * there in the first place. Otherwise, we could wind up trying to access a + * bogus index (like MACH_PORT_DEAD) or zeroing a valid one out. + * + * + */ + if (ms->recv) { + (void)job_assumes_zero(j, runtime_remove_mport(ms->port)); + } } void machservice_resetport(job_t j, struct machservice *ms) { LIST_REMOVE(ms, port_hash_sle); - (void)job_assumes(j, launchd_mport_close_recv(ms->port) == KERN_SUCCESS); - (void)job_assumes(j, launchd_mport_deallocate(ms->port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_close_recv(ms->port)); + (void)job_assumes_zero(j, launchd_mport_deallocate(ms->port)); + ms->gen_num++; - (void)job_assumes(j, launchd_mport_create_recv(&ms->port) == KERN_SUCCESS); - (void)job_assumes(j, launchd_mport_make_send(ms->port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_create_recv(&ms->port)); + (void)job_assumes_zero(j, launchd_mport_make_send(ms->port)); LIST_INSERT_HEAD(&port_hash[HASH_PORT(ms->port)], ms, port_hash_sle); } +void +machservice_stamp_port(job_t j, struct machservice *ms) +{ + mach_port_context_t ctx = 0; + char *where2get = j->prog ? j->prog : j->argv[0]; + + char *prog = NULL; + if ((prog = strrchr(where2get, '/'))) { + prog++; + } else { + prog = where2get; + } + + (void)strncpy((char *)&ctx, prog, sizeof(ctx)); +#if __LITTLE_ENDIAN__ +#if __LP64__ + ctx = OSSwapBigToHostInt64(ctx); +#else + ctx = OSSwapBigToHostInt32(ctx); +#endif +#endif + + (void)job_assumes_zero(j, mach_port_set_context(mach_task_self(), ms->port, ctx)); +} + struct machservice * machservice_new(job_t j, const char *name, mach_port_t *serviceport, bool pid_local) { - struct machservice *ms = calloc(1, sizeof(struct machservice) + strlen(name) + 1); + /* Don't create new MachServices for dead ports. This is primarily for + * clients who use bootstrap_register2(). They can pass in a send right, but + * then that port can immediately go dead. Hilarity ensues. + * + * + */ + if (*serviceport == MACH_PORT_DEAD) { + return NULL; + } + struct machservice *ms = calloc(1, sizeof(struct machservice) + strlen(name) + 1); if (!job_assumes(j, ms != NULL)) { return NULL; } @@ -5988,11 +5895,11 @@ machservice_new(job_t j, const char *name, mach_port_t *serviceport, bool pid_lo ms->per_pid = pid_local; if (likely(*serviceport == MACH_PORT_NULL)) { - if (!job_assumes(j, launchd_mport_create_recv(&ms->port) == KERN_SUCCESS)) { + if (job_assumes_zero(j, launchd_mport_create_recv(&ms->port)) != KERN_SUCCESS) { goto out_bad; } - if (!job_assumes(j, launchd_mport_make_send(ms->port) == KERN_SUCCESS)) { + if (job_assumes_zero(j, launchd_mport_make_send(ms->port)) != KERN_SUCCESS) { goto out_bad2; } *serviceport = ms->port; @@ -6005,33 +5912,36 @@ machservice_new(job_t j, const char *name, mach_port_t *serviceport, bool pid_lo SLIST_INSERT_HEAD(&j->machservices, ms, sle); jobmgr_t where2put = j->mgr; - /* XPC domains are separate from Mach bootstraps. */ + // XPC domains are separate from Mach bootstraps. if (!(j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) { - if (g_flat_mach_namespace && !(j->mgr->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET)) { + if (launchd_flat_mach_namespace && !(j->mgr->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET)) { where2put = root_jobmgr; } } - - /* Don't allow MachServices added by multiple-instance jobs to be looked up by others. - * We could just do this with a simple bit, but then we'd have to uniquify the - * names ourselves to avoid collisions. This is just easier. + + /* Don't allow MachServices added by multiple-instance jobs to be looked up + * by others. We could just do this with a simple bit, but then we'd have to + * uniquify the names ourselves to avoid collisions. This is just easier. */ if (!j->dedicated_instance) { LIST_INSERT_HEAD(&where2put->ms_hash[hash_ms(ms->name)], ms, name_hash_sle); } LIST_INSERT_HEAD(&port_hash[HASH_PORT(ms->port)], ms, port_hash_sle); + if (ms->recv) { + machservice_stamp_port(j, ms); + } + job_log(j, LOG_DEBUG, "Mach service added%s: %s", (j->mgr->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET) ? " to private namespace" : "", name); return ms; out_bad2: - (void)job_assumes(j, launchd_mport_close_recv(ms->port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_close_recv(ms->port)); out_bad: free(ms); return NULL; } -#ifndef __LAUNCH_DISABLE_XPC_SUPPORT__ struct machservice * machservice_new_alias(job_t j, struct machservice *orig) { @@ -6048,7 +5958,6 @@ machservice_new_alias(job_t j, struct machservice *orig) return ms; } -#endif /* __LAUNCH_DISABLE_XPC_SUPPORT__ */ bootstrap_status_t machservice_status(struct machservice *ms) @@ -6094,11 +6003,18 @@ job_setup_exception_port(job_t j, task_t target_task) #endif if (likely(target_task)) { - (void)job_assumes(j, task_set_exception_ports(target_task, EXC_MASK_CRASH, exc_port, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f) == KERN_SUCCESS); + 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); + if (kr) { + if (kr != MACH_SEND_INVALID_DEST) { + (void)job_assumes_zero(j, kr); + } else { + job_log(j, LOG_WARNING, "Task died before exception port could be set."); + } + } } else if (pid1_magic && the_exception_server) { mach_port_t mhp = mach_host_self(); - (void)job_assumes(j, host_set_exception_ports(mhp, EXC_MASK_CRASH, the_exception_server, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f) == KERN_SUCCESS); - job_assumes(j, launchd_mport_deallocate(mhp) == KERN_SUCCESS); + (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, launchd_mport_deallocate(mhp)); } } @@ -6127,7 +6043,7 @@ machservice_setup_options(launch_data_t obj, const char *key, void *context) switch (launch_data_get_type(obj)) { case LAUNCH_DATA_INTEGER: - which_port = (int)launch_data_get_integer(obj); /* XXX we should bound check this... */ + which_port = (int)launch_data_get_integer(obj); // XXX we should bound check this... if (strcasecmp(key, LAUNCH_JOBKEY_MACH_TASKSPECIALPORT) == 0) { switch (which_port) { case TASK_KERNEL_PORT: @@ -6135,7 +6051,8 @@ machservice_setup_options(launch_data_t obj, const char *key, void *context) case TASK_NAME_PORT: case TASK_BOOTSTRAP_PORT: /* I find it a little odd that zero isn't reserved in the header. - * Normally Mach is fairly good about this convention... */ + * Normally Mach is fairly good about this convention... + */ case 0: job_log(ms->job, LOG_WARNING, "Tried to set a reserved task special port: %d", which_port); break; @@ -6146,7 +6063,7 @@ machservice_setup_options(launch_data_t obj, const char *key, void *context) } } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_HOSTSPECIALPORT) == 0 && pid1_magic) { if (which_port > HOST_MAX_SPECIAL_KERNEL_PORT) { - (void)job_assumes(ms->job, (errno = host_set_special_port(mhp, which_port, ms->port)) == KERN_SUCCESS); + (void)job_assumes_zero(ms->job, (errno = host_set_special_port(mhp, which_port, ms->port))); } else { job_log(ms->job, LOG_WARNING, "Tried to set a reserved host special port: %d", which_port); } @@ -6163,9 +6080,7 @@ machservice_setup_options(launch_data_t obj, const char *key, void *context) job_set_exception_port(ms->job, ms->port); } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_KUNCSERVER) == 0) { ms->kUNCServer = b; - (void)job_assumes(ms->job, host_set_UNDServer(mhp, ms->port) == KERN_SUCCESS); - } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_PINGEVENTUPDATES) == 0) { - ms->event_update_port = b; + (void)job_assumes_zero(ms->job, host_set_UNDServer(mhp, ms->port)); } break; case LAUNCH_DATA_STRING: @@ -6185,7 +6100,7 @@ machservice_setup_options(launch_data_t obj, const char *key, void *context) break; } - job_assumes(ms->job, launchd_mport_deallocate(mhp) == KERN_SUCCESS); + (void)job_assumes_zero(ms->job, launchd_mport_deallocate(mhp)); } void @@ -6206,7 +6121,7 @@ machservice_setup(launch_data_t obj, const char *key, void *context) ms->isActive = false; ms->upfront = true; - + if (launch_data_get_type(obj) == LAUNCH_DATA_DICTIONARY) { launch_data_dict_iterate(obj, machservice_setup_options, ms); } @@ -6223,7 +6138,7 @@ jobmgr_do_garbage_collection(jobmgr_t jm) if (!jm->shutting_down) { return jm; } - + if (SLIST_EMPTY(&jm->submgrs)) { jobmgr_log(jm, LOG_DEBUG, "No submanagers left."); } else { @@ -6239,8 +6154,8 @@ jobmgr_do_garbage_collection(jobmgr_t jm) if (ji->anonymous) { continue; } - - /* Let the shutdown monitor be up until the very end. */ + + // Let the shutdown monitor be up until the very end. if (ji->shutdown_monitor) { continue; } @@ -6260,19 +6175,7 @@ jobmgr_do_garbage_collection(jobmgr_t jm) job_log(ji, LOG_DEBUG, "Job is active: %s", active); job_stop(ji); - if (ji->p && !ji->dirty_at_shutdown) { - /* We really only care if the job has not yet been reaped. - * There's no reason to delay shutdown if a Mach port has not - * yet been sent back to us, for example. While we're shutting - * all the "normal" jobs down, do not count the - * dirty-at-shutdown jobs toward the total of actives. - * - * Note that there's a potential race here where we may not get - * a port back in time, so that when we hit jobmgr_remove(), we - * end up removing the job and then our attempt to close the - * Mach port will fail. But at that point, the failure won't - * even make it to the syslog, so not a big deal. - */ + if (!ji->dirty_at_shutdown) { actives++; } @@ -6287,44 +6190,68 @@ jobmgr_do_garbage_collection(jobmgr_t jm) jm->shutdown_jobs_dirtied = true; if (actives == 0) { if (!jm->shutdown_jobs_cleaned) { + /* Once all normal jobs have exited, we clean the dirty-at-shutdown + * jobs and make them into normal jobs so that the above loop will + * handle them appropriately. + */ LIST_FOREACH(ji, &jm->jobs, sle) { - if (!ji->anonymous) { - job_close_shutdown_transaction(ji); - actives++; + if (ji->anonymous) { + continue; + } + + if (!job_active(ji)) { + continue; } + + if (ji->shutdown_monitor) { + continue; + } + + job_close_shutdown_transaction(ji); + actives++; } jm->shutdown_jobs_cleaned = true; - } else if (jm->monitor_shutdown && _s_shutdown_monitor) { - /* The rest of shutdown has completed, so we can kill the shutdown - * monitor now like it was any other job. - */ - _s_shutdown_monitor->shutdown_monitor = false; - actives = 1; + } - job_log(_s_shutdown_monitor, LOG_NOTICE | LOG_CONSOLE, "Stopping shutdown monitor."); - job_stop(_s_shutdown_monitor); - _s_shutdown_monitor = NULL; + if (SLIST_EMPTY(&jm->submgrs) && actives == 0) { + /* We may be in a situation where the shutdown monitor is all that's + * left, in which case we want to stop it. Like dirty-at-shutdown + * jobs, we turn it back into a normal job so that the main loop + * treats it appropriately. + * + * See: + * + * + * + */ + if (jm->monitor_shutdown && _launchd_shutdown_monitor) { + /* The rest of shutdown has completed, so we can kill the shutdown + * monitor now like it was any other job. + */ + _launchd_shutdown_monitor->shutdown_monitor = false; + + job_log(_launchd_shutdown_monitor, LOG_NOTICE | LOG_CONSOLE, "Stopping shutdown monitor."); + job_stop(_launchd_shutdown_monitor); + _launchd_shutdown_monitor = NULL; + } else { + jobmgr_log(jm, LOG_DEBUG, "Removing."); + jobmgr_remove(jm); + return NULL; + } } } - jobmgr_t r = jm; - if (SLIST_EMPTY(&jm->submgrs) && actives == 0) { - jobmgr_log(jm, LOG_DEBUG, "Removing."); - jobmgr_remove(jm); - r = NULL; - } - - return r; + return jm; } void jobmgr_kill_stray_children(jobmgr_t jm, pid_t *p, size_t np) { - /* I maintain that stray processes should be at the mercy of launchd during shutdown, - * but nevertheless, things like diskimages-helper can stick around, and SIGKILLing - * them can result in data loss. So we send SIGTERM to all the strays and don't wait - * for them to exit before moving on. + /* I maintain that stray processes should be at the mercy of launchd during + * shutdown, but nevertheless, things like diskimages-helper can stick + * around, and SIGKILLing them can result in data loss. So we send SIGTERM + * to all the strays and don't wait for them to exit before moving on. * * See rdar://problem/6562592 */ @@ -6332,7 +6259,7 @@ jobmgr_kill_stray_children(jobmgr_t jm, pid_t *p, size_t np) for (i = 0; i < np; i++) { if (p[i] != 0) { jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "Sending SIGTERM to PID %u and continuing...", p[i]); - (void)jobmgr_assumes(jm, runtime_kill(p[i], SIGTERM) != -1); + (void)jobmgr_assumes_zero_p(jm, kill2(p[i], SIGTERM)); } } } @@ -6343,7 +6270,7 @@ jobmgr_log_stray_children(jobmgr_t jm, bool kill_strays) size_t kp_skipped = 0, len = sizeof(pid_t) * get_kern_max_proc(); pid_t *pids = NULL; int i = 0, kp_cnt = 0; - + if (likely(jm->parentmgr || !pid1_magic)) { return; } @@ -6354,7 +6281,7 @@ jobmgr_log_stray_children(jobmgr_t jm, bool kill_strays) runtime_ktrace0(RTKT_LAUNCHD_FINDING_ALL_STRAYS); - if (!jobmgr_assumes(jm, (kp_cnt = proc_listallpids(pids, len)) != -1)) { + if (jobmgr_assumes_zero_p(jm, (kp_cnt = proc_listallpids(pids, len))) == -1) { goto out; } @@ -6363,13 +6290,13 @@ jobmgr_log_stray_children(jobmgr_t jm, bool kill_strays) struct proc_bsdshortinfo proc; if (proc_pidinfo(pids[i], PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) { if (errno != ESRCH) { - jobmgr_assumes(jm, errno == 0); + (void)jobmgr_assumes_zero(jm, errno); } kp_skipped++; continue; } - + pid_t p_i = pids[i]; pid_t pp_i = proc.pbsi_ppid; pid_t pg_i = proc.pbsi_pgid; @@ -6381,19 +6308,19 @@ jobmgr_log_stray_children(jobmgr_t jm, bool kill_strays) continue; } - if (_s_shutdown_monitor && pp_i == _s_shutdown_monitor->p) { + if (_launchd_shutdown_monitor && pp_i == _launchd_shutdown_monitor->p) { kp_skipped++; continue; } - - /* We might have some jobs hanging around that we've decided to shut down in spite of. */ + + // We might have some jobs hanging around that we've decided to shut down in spite of. job_t j = jobmgr_find_by_pid(jm, p_i, false); if (!j || (j && j->anonymous)) { jobmgr_log(jm, LOG_INFO | LOG_CONSOLE, "Stray %s%s at shutdown: PID %u PPID %u PGID %u %s", z, j ? "anonymous job" : "process", p_i, pp_i, pg_i, n); - + int status = 0; if (pp_i == getpid() && !jobmgr_assumes(jm, proc.pbsi_status != SZOMB)) { - if (jobmgr_assumes(jm, waitpid(p_i, &status, WNOHANG) == 0)) { + if (jobmgr_assumes_zero(jm, waitpid(p_i, &status, WNOHANG)) == 0) { jobmgr_log(jm, LOG_INFO | LOG_CONSOLE, "Unreaped zombie stray exited with status %i.", WEXITSTATUS(status)); } kp_skipped++; @@ -6439,18 +6366,17 @@ job_uncork_fork(job_t j) /* this unblocks the child and avoids a race * between the above fork() and the kevent_mod() */ (void)job_assumes(j, write(j->fork_fd, &c, sizeof(c)) == sizeof(c)); - (void)job_assumes(j, runtime_close(j->fork_fd) != -1); + (void)job_assumes_zero_p(j, runtime_close(j->fork_fd)); j->fork_fd = 0; } jobmgr_t jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag, const char *name, bool skip_init, mach_port_t asport) { - mach_msg_size_t mxmsgsz; job_t bootstrapper = NULL; jobmgr_t jmr; - launchd_assert(offsetof(struct jobmgr_s, kqjobmgr_callback) == 0); + __OSX_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"); @@ -6476,7 +6402,7 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo SLIST_INSERT_HEAD(&jm->submgrs, jmr, sle); } - if (jm && !jobmgr_assumes(jmr, launchd_mport_notify_req(jmr->req_port, MACH_NOTIFY_DEAD_NAME) == KERN_SUCCESS)) { + if (jm && jobmgr_assumes_zero(jmr, launchd_mport_notify_req(jmr->req_port, MACH_NOTIFY_DEAD_NAME)) != KERN_SUCCESS) { goto out_bad; } @@ -6489,7 +6415,7 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo snprintf(service_buf, sizeof(service_buf), "com.apple.launchd.peruser.%u", getuid()); - if (!jobmgr_assumes(jmr, bootstrap_check_in(bootstrap_port, service_buf, &jmr->jm_port) == 0)) { + if (jobmgr_assumes_zero(jmr, bootstrap_check_in(bootstrap_port, service_buf, &jmr->jm_port)) != 0) { goto out_bad; } @@ -6497,21 +6423,21 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo int dfd, lfd = (int) strtol(trusted_fd, NULL, 10); if ((dfd = dup(lfd)) >= 0) { - (void)jobmgr_assumes(jmr, runtime_close(dfd) != -1); - (void)jobmgr_assumes(jmr, runtime_close(lfd) != -1); + (void)jobmgr_assumes_zero_p(jmr, runtime_close(dfd)); + (void)jobmgr_assumes_zero_p(jmr, runtime_close(lfd)); } unsetenv(LAUNCHD_TRUSTED_FD_ENV); } - /* cut off the Libc cache, we don't want to deadlock against ourself */ + // cut off the Libc cache, we don't want to deadlock against ourself inherited_bootstrap_port = bootstrap_port; bootstrap_port = MACH_PORT_NULL; - launchd_assert(launchd_mport_notify_req(inherited_bootstrap_port, MACH_NOTIFY_DEAD_NAME) == KERN_SUCCESS); + osx_assert_zero(launchd_mport_notify_req(inherited_bootstrap_port, MACH_NOTIFY_DEAD_NAME)); - /* We set this explicitly as we start each child */ - launchd_assert(launchd_set_bport(MACH_PORT_NULL) == KERN_SUCCESS); - } else if (!jobmgr_assumes(jmr, launchd_mport_create_recv(&jmr->jm_port) == KERN_SUCCESS)) { + // We set this explicitly as we start each child + osx_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; } @@ -6519,28 +6445,11 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo sprintf(jmr->name_init, "%u", MACH_PORT_INDEX(jmr->jm_port)); } - /* Sigh... at the moment, MIG has maxsize == sizeof(reply union) */ - mxmsgsz = (typeof(mxmsgsz)) sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem); - if (job_mig_protocol_vproc_subsystem.maxsize > mxmsgsz) { - mxmsgsz = job_mig_protocol_vproc_subsystem.maxsize; - } - - /* Total hacks. But the MIG server loop is too generic, and the more dynamic - * parts of it haven't been tested, or if they have, it was a very long time - * ago. - */ - if (xpc_events_xpc_events_subsystem.maxsize > mxmsgsz) { - mxmsgsz = xpc_events_xpc_events_subsystem.maxsize; - } - if (xpc_domain_xpc_domain_subsystem.maxsize > mxmsgsz) { - mxmsgsz = xpc_domain_xpc_domain_subsystem.maxsize; - } - if (!jm) { - (void)jobmgr_assumes(jmr, kevent_mod(SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr) != -1); - (void)jobmgr_assumes(jmr, kevent_mod(SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr) != -1); - (void)jobmgr_assumes(jmr, kevent_mod(SIGUSR2, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr) != -1); - (void)jobmgr_assumes(jmr, kevent_mod(0, EVFILT_FS, EV_ADD, VQ_MOUNT|VQ_UNMOUNT|VQ_UPDATE, 0, jmr) != -1); + (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(0, EVFILT_FS, EV_ADD, VQ_MOUNT|VQ_UNMOUNT|VQ_UPDATE, 0, jmr)); } if (name && !skip_init) { @@ -6548,7 +6457,7 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo } if (!bootstrapper || !bootstrapper->weird_bootstrap) { - if (!jobmgr_assumes(jmr, runtime_add_mport(jmr->jm_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS)) { + if (jobmgr_assumes_zero(jmr, runtime_add_mport(jmr->jm_port, job_server)) != KERN_SUCCESS) { goto out_bad; } } @@ -6557,7 +6466,7 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo if (bootstrapper) { bootstrapper->asport = asport; - + jobmgr_log(jmr, LOG_DEBUG, "Bootstrapping new job manager with audit session %u", asport); (void)jobmgr_assumes(jmr, job_dispatch(bootstrapper, true) != NULL); } else { @@ -6565,7 +6474,7 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo } if (asport != MACH_PORT_NULL) { - (void)jobmgr_assumes(jmr, launchd_mport_copy_send(asport) == KERN_SUCCESS); + (void)jobmgr_assumes_zero(jmr, launchd_mport_copy_send(asport)); } if (jmr->parentmgr) { @@ -6584,7 +6493,6 @@ out_bad: return NULL; } -#ifndef __LAUNCH_DISABLE_XPC_SUPPORT__ jobmgr_t jobmgr_new_xpc_singleton_domain(jobmgr_t jm, name_t name) { @@ -6594,7 +6502,7 @@ jobmgr_new_xpc_singleton_domain(jobmgr_t jm, name_t name) * bootstrap port as their requestor ports so they'll never go away. */ mach_port_t req_port = root_jobmgr->jm_port; - if (jobmgr_assumes(jm, launchd_mport_make_send(req_port) == KERN_SUCCESS)) { + if (jobmgr_assumes_zero(jm, launchd_mport_make_send(req_port)) == KERN_SUCCESS) { new = jobmgr_new(root_jobmgr, req_port, MACH_PORT_NULL, false, name, true, MACH_PORT_NULL); if (new) { new->properties |= BOOTSTRAP_PROPERTY_XPC_SINGLETON; @@ -6625,8 +6533,8 @@ jobmgr_find_xpc_per_user_domain(jobmgr_t jm, uid_t uid) */ job_t puj = jobmgr_lookup_per_user_context_internal(NULL, uid, &jmi->req_bsport); if (jobmgr_assumes(jmi, puj != NULL)) { - (void)jobmgr_assumes(jmi, launchd_mport_copy_send(puj->asport) == KERN_SUCCESS); - (void)jobmgr_assumes(jmi, launchd_mport_copy_send(jmi->req_bsport) == KERN_SUCCESS); + (void)jobmgr_assumes_zero(jmi, launchd_mport_copy_send(puj->asport)); + (void)jobmgr_assumes_zero(jmi, launchd_mport_copy_send(jmi->req_bsport)); jmi->shortdesc = "per-user"; jmi->req_asport = puj->asport; jmi->req_asid = puj->asid; @@ -6656,10 +6564,10 @@ jobmgr_find_xpc_per_session_domain(jobmgr_t jm, au_asid_t asid) (void)snprintf(name, sizeof(name), "com.apple.xpc.domain.persession.%i", asid); jmi = jobmgr_new_xpc_singleton_domain(jm, name); if (jobmgr_assumes(jm, jmi != NULL)) { - (void)jobmgr_assumes(jmi, launchd_mport_make_send(root_jobmgr->jm_port) == KERN_SUCCESS); + (void)jobmgr_assumes_zero(jmi, launchd_mport_make_send(root_jobmgr->jm_port)); jmi->shortdesc = "per-session"; jmi->req_bsport = root_jobmgr->jm_port; - (void)jobmgr_assumes(jmi, audit_session_port(asid, &jmi->req_asport) == 0); + (void)jobmgr_assumes_zero(jmi, audit_session_port(asid, &jmi->req_asport)); jmi->req_asid = asid; jmi->req_euid = -1; jmi->req_egid = -1; @@ -6671,7 +6579,6 @@ jobmgr_find_xpc_per_session_domain(jobmgr_t jm, au_asid_t asid) return jmi; } -#endif /* __LAUNCH_DISABLE_XPC_SUPPORT__ */ job_t jobmgr_init_session(jobmgr_t jm, const char *session_type, bool sflag) @@ -6682,25 +6589,28 @@ jobmgr_init_session(jobmgr_t jm, const char *session_type, bool sflag) snprintf(thelabel, sizeof(thelabel), "com.apple.launchctl.%s", session_type); bootstrapper = job_new(jm, thelabel, NULL, bootstrap_tool); - + if (jobmgr_assumes(jm, bootstrapper != NULL) && (jm->parentmgr || !pid1_magic)) { bootstrapper->is_bootstrapper = true; char buf[100]; - /* launchd-201: can't ssh in with AFP OD account (hangs) */ + // launchd-201: can't ssh in with AFP OD account (hangs) snprintf(buf, sizeof(buf), "0x%X:0:0", getuid()); - envitem_new(bootstrapper, "__CF_USER_TEXT_ENCODING", buf, false, false); + envitem_new(bootstrapper, "__CF_USER_TEXT_ENCODING", buf, false); bootstrapper->weird_bootstrap = true; (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; +#endif bootstrapper->is_bootstrapper = true; if (jobmgr_assumes(jm, pid1_magic)) { - /* Have our system bootstrapper print out to the console. */ + // Have our system bootstrapper print out to the console. bootstrapper->stdoutpath = strdup(_PATH_CONSOLE); bootstrapper->stderrpath = strdup(_PATH_CONSOLE); - if (g_console) { - (void)jobmgr_assumes(jm, kevent_mod((uintptr_t)fileno(g_console), EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_REVOKE, 0, jm) != -1); + if (launchd_console) { + (void)jobmgr_assumes_zero_p(jm, kevent_mod((uintptr_t)fileno(launchd_console), EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_REVOKE, 0, jm)); } } } @@ -6716,18 +6626,17 @@ jobmgr_delete_anything_with_port(jobmgr_t jm, mach_port_t port) jobmgr_t jmi, jmn; /* Mach ports, unlike Unix descriptors, are reference counted. In other - * words, when some program hands us a second or subsequent send right - * to a port we already have open, the Mach kernel gives us the same - * port number back and increments an reference count associated with - * the port. This forces us, when discovering that a receive right at - * the other end has been deleted, to wander all of our objects to see - * what weird places clients might have handed us the same send right - * to use. + * words, when some program hands us a second or subsequent send right to a + * port we already have open, the Mach kernel gives us the same port number + * back and increments an reference count associated with the port. This + * This forces us, when discovering that a receive right at the other end + * has been deleted, to wander all of our objects to see what weird places + * clients might have handed us the same send right to use. */ if (jm == root_jobmgr) { if (port == inherited_bootstrap_port) { - (void)jobmgr_assumes(jm, launchd_mport_deallocate(port) == KERN_SUCCESS); + (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(port)); inherited_bootstrap_port = MACH_PORT_NULL; return jobmgr_shutdown(jm); @@ -6764,16 +6673,16 @@ jobmgr_lookup_service(jobmgr_t jm, const char *name, bool check_parent, pid_t ta /* This is a hack to let FileSyncAgent look up per-PID Mach services from the Background * bootstrap in other bootstraps. */ - - /* Start in the given bootstrap. */ + + // Start in the given bootstrap. if (unlikely((target_j = jobmgr_find_by_pid(jm, target_pid, false)) == NULL)) { - /* If we fail, do a deep traversal. */ + // If we fail, do a deep traversal. if (unlikely((target_j = jobmgr_find_by_pid_deep(root_jobmgr, target_pid, true)) == NULL)) { jobmgr_log(jm, LOG_DEBUG, "Didn't find PID %i", target_pid); return NULL; } } - + SLIST_FOREACH(ms, &target_j->machservices, sle) { if (ms->per_pid && strcmp(name, ms->name) == 0) { return ms; @@ -6783,15 +6692,15 @@ jobmgr_lookup_service(jobmgr_t jm, const char *name, bool check_parent, pid_t ta job_log(target_j, LOG_DEBUG, "Didn't find per-PID Mach service: %s", name); return NULL; } - + jobmgr_t where2look = jm; - /* XPC domains are separate from Mach bootstraps. */ + // XPC domains are separate from Mach bootstraps. if (!(jm->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) { - if (g_flat_mach_namespace && !(jm->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET)) { + if (launchd_flat_mach_namespace && !(jm->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET)) { where2look = root_jobmgr; } } - + LIST_FOREACH(ms, &where2look->ms_hash[hash_ms(name)], name_hash_sle) { if (!ms->per_pid && strcmp(name, ms->name) == 0) { return ms; @@ -6840,20 +6749,20 @@ machservice_drain_port(struct machservice *ms) { bool drain_one = ms->drain_one_on_crash; bool drain_all = ms->drain_all_on_crash; - + if (!job_assumes(ms->job, (drain_one || drain_all) == true)) { return; } job_log(ms->job, LOG_INFO, "Draining %s...", ms->name); - + char req_buff[sizeof(union __RequestUnion__catch_mach_exc_subsystem) * 2]; char rep_buff[sizeof(union __ReplyUnion__catch_mach_exc_subsystem)]; mig_reply_error_t *req_hdr = (mig_reply_error_t *)&req_buff; mig_reply_error_t *rep_hdr = (mig_reply_error_t *)&rep_buff; mach_msg_return_t mr = ~MACH_MSG_SUCCESS; - + do { /* This should be a direct check on the Mach service to see if it's an exception-handling * port, and it will break things if ReportCrash or SafetyNet start advertising other @@ -6873,7 +6782,7 @@ machservice_drain_port(struct machservice *ms) case MACH_RCV_TIMED_OUT: break; case MACH_RCV_TOO_LARGE: - runtime_syslog(LOG_WARNING, "Tried to receive message that was larger than %lu bytes", sizeof(req_buff)); + launchd_syslog(LOG_WARNING, "Tried to receive message that was larger than %lu bytes", sizeof(req_buff)); break; default: break; @@ -6898,15 +6807,15 @@ machservice_delete(job_t j, struct machservice *ms, bool port_died) if (unlikely(ms->debug_on_close)) { job_log(j, LOG_NOTICE, "About to enter kernel debugger because of Mach port: 0x%x", ms->port); - (void)job_assumes(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER) == KERN_SUCCESS); + (void)job_assumes_zero(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER)); } if (ms->recv && job_assumes(j, !machservice_active(ms))) { job_log(j, LOG_DEBUG, "Closing receive right for %s", ms->name); - (void)job_assumes(j, launchd_mport_close_recv(ms->port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_close_recv(ms->port)); } - (void)job_assumes(j, launchd_mport_deallocate(ms->port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_deallocate(ms->port)); if (unlikely(ms->port == the_exception_server)) { the_exception_server = 0; @@ -6939,11 +6848,11 @@ machservice_request_notifications(struct machservice *ms) job_checkin(ms->job); } - (void)job_assumes(ms->job, launchd_mport_notify_req(ms->port, which) == KERN_SUCCESS); + (void)job_assumes_zero(ms->job, launchd_mport_notify_req(ms->port, which)); } -#define NELEM(x) (sizeof(x)/sizeof(x[0])) -#define END_OF(x) (&(x)[NELEM(x)]) +#define NELEM(x) (sizeof(x)/sizeof(x[0])) +#define END_OF(x) (&(x)[NELEM(x)]) char ** mach_cmd2argv(const char *string) @@ -6979,7 +6888,8 @@ mach_cmd2argv(const char *string) argv_ret = malloc((nargs + 1) * sizeof(char *) + strlen(string) + 1); - if (!launchd_assumes(argv_ret != NULL)) { + if (!argv_ret) { + (void)osx_assumes_zero(errno); return NULL; } @@ -6991,7 +6901,7 @@ mach_cmd2argv(const char *string) co += strlen(argv[i]) + 1; } argv_ret[i] = NULL; - + return argv_ret; } @@ -7003,7 +6913,7 @@ job_checkin(job_t j) bool job_is_god(job_t j) { - return j->embedded_special_privileges; + return j->embedded_god; } bool @@ -7018,22 +6928,24 @@ job_ack_port_destruction(mach_port_t p) } } - if (!jobmgr_assumes(root_jobmgr, ms != NULL)) { + if (!ms) { + launchd_syslog(LOG_WARNING, "Could not find MachService to match receive right: 0x%x", p); return false; } j = ms->job; jobmgr_log(root_jobmgr, LOG_DEBUG, "Receive right returned to us: %s", ms->name); - - /* Without being the exception handler, NOTE_EXIT is our only way to tell if the job - * crashed, and we can't rely on NOTE_EXIT always being processed after all the job's - * receive rights have been returned. + + /* Without being the exception handler, NOTE_EXIT is our only way to tell if + * the job crashed, and we can't rely on NOTE_EXIT always being processed + * after all the job's receive rights have been returned. * - * So when we get receive rights back, check to see if the job has been reaped yet. If - * not, then we add this service to a list of services to be drained on crash if it's - * requested that behavior. So, for a job with N receive rights all requesting that they - * be drained on crash, we can safely handle the following sequence of events. + * So when we get receive rights back, check to see if the job has been + * reaped yet. If not, then we add this service to a list of services to be + * drained on crash if it's requested that behavior. So, for a job with N + * receive rights all requesting that they be drained on crash, we can + * safely handle the following sequence of events. * * ReceiveRight0Returned * ReceiveRight1Returned @@ -7045,7 +6957,6 @@ job_ack_port_destruction(mach_port_t p) * . * ReceiveRight(N - 1)Returned */ - if (ms->drain_one_on_crash || ms->drain_all_on_crash) { if (j->crashed && j->reaped) { job_log(j, LOG_DEBUG, "Job has crashed. Draining port..."); @@ -7054,24 +6965,15 @@ job_ack_port_destruction(mach_port_t p) job_log(j, LOG_DEBUG, "Job's exit status is still unknown. Deferring drain."); } } - - /* If we get this notification after the job has been reaped, then we want to ping - * the event port to keep things going. - */ - if (ms->event_update_port && !j->p && job_assumes(j, j->event_monitor)) { - if (_s_event_update_port == MACH_PORT_NULL) { - (void)job_assumes(j, launchd_mport_make_send_once(ms->port, &_s_event_update_port) == KERN_SUCCESS); - } - eventsystem_ping(); - } - + ms->isActive = false; if (ms->delete_on_destruction) { machservice_delete(j, ms, false); } else if (ms->reset) { machservice_resetport(j, ms); } - + + machservice_stamp_port(j, ms); job_dispatch(j, false); root_jobmgr = jobmgr_do_garbage_collection(root_jobmgr); @@ -7084,7 +6986,7 @@ job_ack_no_senders(job_t j) { j->priv_port_has_senders = false; - (void)job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_close_recv(j->j_port)); j->j_port = 0; job_log(j, LOG_DEBUG, "No more senders on privileged Mach bootstrap port"); @@ -7102,11 +7004,10 @@ semaphoreitem_new(job_t j, semaphore_reason_t why, const char *what) alloc_sz += strlen(what) + 1; } - if (!job_assumes(j, si = calloc(1, alloc_sz))) { + if (job_assumes(j, si = calloc(1, alloc_sz)) == NULL) { return false; } - si->fd = -1; si->why = why; if (what) { @@ -7114,7 +7015,7 @@ semaphoreitem_new(job_t j, semaphore_reason_t why, const char *what) } SLIST_INSERT_HEAD(&j->semaphores, si, sle); - + if ((why == OTHER_JOB_ENABLED || why == OTHER_JOB_DISABLED) && !j->nosy) { job_log(j, LOG_DEBUG, "Job is interested in \"%s\".", what); SLIST_INSERT_HEAD(&s_curious_jobs, j, curious_jobs_sle); @@ -7160,16 +7061,12 @@ semaphoreitem_delete(job_t j, struct semaphoreitem *si) SLIST_REMOVE(&j->semaphores, si, semaphoreitem, sle); - if (si->fd != -1) { - (void)job_assumes(j, runtime_close(si->fd) != -1); - } - - /* We'll need to rethink this if it ever becomes possible to dynamically add or remove semaphores. */ + // We'll need to rethink this if it ever becomes possible to dynamically add or remove semaphores. if ((si->why == OTHER_JOB_ENABLED || si->why == OTHER_JOB_DISABLED) && j->nosy) { j->nosy = false; SLIST_REMOVE(&s_curious_jobs, j, job_s, curious_jobs_sle); } - + free(si); } @@ -7207,68 +7104,71 @@ semaphoreitem_setup(launch_data_t obj, const char *key, void *context) semaphoreitem_new(j, why, NULL); j->start_pending = true; } else { - (void)job_assumes(j, false); + job_log(j, LOG_ERR, "Unrecognized KeepAlive attribute: %s", key); } break; case LAUNCH_DATA_DICTIONARY: - if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE) == 0) { - sdic.why_true = PATH_EXISTS; - sdic.why_false = PATH_MISSING; - } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE) == 0) { + if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE) == 0) { sdic.why_true = OTHER_JOB_ACTIVE; sdic.why_false = OTHER_JOB_INACTIVE; } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED) == 0) { sdic.why_true = OTHER_JOB_ENABLED; sdic.why_false = OTHER_JOB_DISABLED; } else { - (void)job_assumes(j, false); + job_log(j, LOG_ERR, "Unrecognized KeepAlive attribute: %s", key); break; } launch_data_dict_iterate(obj, semaphoreitem_setup_dict_iter, &sdic); break; default: - (void)job_assumes(j, false); + job_log(j, LOG_ERR, "Unrecognized KeepAlive type: %u", launch_data_get_type(obj)); break; } } bool -externalevent_new(job_t j, struct eventsystem *sys, char *evname, launch_data_t event) +externalevent_new(job_t j, struct eventsystem *sys, const char *evname, xpc_object_t event) { + if (j->event_monitor) { + job_log(j, LOG_ERR, "The event monitor job cannot use LaunchEvents or XPC Events."); + return false; + } + struct externalevent *ee = (struct externalevent *)calloc(1, sizeof(struct externalevent) + strlen(evname) + 1); - if (job_assumes(j, ee != NULL)) { - ee->event = launch_data_copy(event); - if (job_assumes(j, ee->event != NULL)) { - strcpy(ee->name, evname); - ee->job = j; - ee->id = sys->curid; - ee->sys = sys; - ee->state = false; - ee->wanted_state = true; - sys->curid++; - - LIST_INSERT_HEAD(&j->events, ee, job_le); - LIST_INSERT_HEAD(&sys->events, ee, sys_le); - - job_log(j, LOG_DEBUG, "New event: %s:%s", sys->name, evname); - } else { - free(ee); - ee = NULL; - } + if (!ee) { + return false; + } + + ee->event = xpc_retain(event); + (void)strcpy(ee->name, evname); + ee->job = j; + ee->id = sys->curid; + ee->sys = sys; + ee->state = false; + ee->wanted_state = true; + sys->curid++; + + if (sys == _launchd_support_system) { + ee->internal = true; } + LIST_INSERT_HEAD(&j->events, ee, job_le); + LIST_INSERT_HEAD(&sys->events, ee, sys_le); + + job_log(j, LOG_DEBUG, "New event: %s/%s", sys->name, evname); + eventsystem_ping(); - return ee; + return true; } void externalevent_delete(struct externalevent *ee) { - launch_data_free(ee->event); + xpc_release(ee->event); LIST_REMOVE(ee, job_le); LIST_REMOVE(ee, sys_le); - + free(ee); eventsystem_ping(); @@ -7277,24 +7177,37 @@ externalevent_delete(struct externalevent *ee) void externalevent_setup(launch_data_t obj, const char *key, void *context) { + /* This method can ONLY be called on the job_import() path, as it assumes + * the input is a launch_data_t. + */ struct externalevent_iter_ctx *ctx = (struct externalevent_iter_ctx *)context; - (void)job_assumes(ctx->j, externalevent_new(ctx->j, ctx->sys, (char *)key, obj)); + + 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); + xpc_release(xobj); + } else { + job_log(ctx->j, LOG_ERR, "Could not import event for job: %s", key); + } } struct externalevent * externalevent_find(const char *sysname, uint64_t id) { struct externalevent *ei = NULL; - + struct eventsystem *es = eventsystem_find(sysname); - if (launchd_assumes(es != NULL)) { + if (es != NULL) { LIST_FOREACH(ei, &es->events, sys_le) { if (ei->id == id) { break; } } + } else { + launchd_syslog(LOG_ERR, "Could not find event system: %s", sysname); } - + return ei; } @@ -7302,9 +7215,12 @@ struct eventsystem * eventsystem_new(const char *name) { struct eventsystem *es = (struct eventsystem *)calloc(1, sizeof(struct eventsystem) + strlen(name) + 1); - if (launchd_assumes(es != NULL)) { - strcpy(es->name, name); + if (es != NULL) { + es->curid = 1; + (void)strcpy(es->name, name); LIST_INSERT_HEAD(&_s_event_systems, es, global_le); + } else { + (void)osx_assumes_zero(errno); } return es; @@ -7317,9 +7233,9 @@ eventsystem_delete(struct eventsystem *es) while ((ei = LIST_FIRST(&es->events))) { externalevent_delete(ei); } - + LIST_REMOVE(es, global_le); - + free(es); } @@ -7330,20 +7246,21 @@ eventsystem_setup(launch_data_t obj, const char *key, void *context) if (!job_assumes(j, launch_data_get_type(obj) == LAUNCH_DATA_DICTIONARY)) { return; } - + struct eventsystem *sys = eventsystem_find(key); if (unlikely(sys == NULL)) { sys = eventsystem_new(key); job_log(j, LOG_DEBUG, "New event system: %s", key); } - + if (job_assumes(j, sys != NULL)) { struct externalevent_iter_ctx ctx = { .j = j, .sys = sys, }; + + job_log(j, LOG_DEBUG, "Importing events for stream: %s", key); launch_data_dict_iterate(obj, externalevent_setup, &ctx); - sys->has_updates = true; } } @@ -7356,25 +7273,23 @@ eventsystem_find(const char *name) break; } } - + return esi; } void eventsystem_ping(void) { - /* We don't wrap this in an assumes() macro because we could potentially - * call this function many times before the helper job gets back to us - * and gives us another send-once right. So if it's MACH_PORT_NULL, that - * means that we've sent a ping, but the helper hasn't yet checked in to - * get the new set of notifications. - */ - if (_s_event_update_port != MACH_PORT_NULL) { - kern_return_t kr = helper_downcall_ping(_s_event_update_port); - if (kr != KERN_SUCCESS) { - runtime_syslog(LOG_NOTICE, "helper_downcall_ping(): kr = 0x%x", kr); + if (!_launchd_event_monitor) { + return; + } + + if (!_launchd_event_monitor->p) { + (void)job_dispatch(_launchd_event_monitor, true); + } else { + if (_launchd_event_monitor->event_monitor_ready2signal) { + (void)job_assumes_zero_p(_launchd_event_monitor, kill(_launchd_event_monitor->p, SIGUSR1)); } - _s_event_update_port = MACH_PORT_NULL; } } @@ -7568,58 +7483,13 @@ cronemu_min(struct tm *wtm, int min) return true; } -kern_return_t -job_mig_setup_shmem(job_t j, mach_port_t *shmem_port) -{ - memory_object_size_t size_of_page, size_of_page_orig; - vm_address_t vm_addr; - kern_return_t kr; - - if (!launchd_assumes(j != NULL)) { - return BOOTSTRAP_NO_MEMORY; - } - - if (unlikely(j->anonymous)) { - job_log(j, LOG_DEBUG, "Anonymous job tried to setup shared memory"); - return BOOTSTRAP_NOT_PRIVILEGED; - } - - if (unlikely(j->shmem)) { - job_log(j, LOG_ERR, "Tried to setup shared memory more than once"); - return BOOTSTRAP_NOT_PRIVILEGED; - } - - size_of_page_orig = size_of_page = getpagesize(); - - kr = vm_allocate(mach_task_self(), &vm_addr, size_of_page, true); - - if (!job_assumes(j, kr == 0)) { - return kr; - } - - j->shmem = (typeof(j->shmem))vm_addr; - j->shmem->vp_shmem_standby_timeout = j->timeout; - - kr = mach_make_memory_entry_64(mach_task_self(), &size_of_page, - (memory_object_offset_t)vm_addr, VM_PROT_READ|VM_PROT_WRITE, shmem_port, 0); - - if (job_assumes(j, kr == 0)) { - (void)job_assumes(j, size_of_page == size_of_page_orig); - } - - /* no need to inherit this in child processes */ - (void)job_assumes(j, vm_inherit(mach_task_self(), (vm_address_t)j->shmem, size_of_page_orig, VM_INHERIT_NONE) == 0); - - return kr; -} - kern_return_t job_mig_create_server(job_t j, cmd_t server_cmd, uid_t server_uid, boolean_t on_demand, mach_port_t *server_portp) { struct ldcred *ldc = runtime_get_caller_creds(); job_t js; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -7651,7 +7521,7 @@ job_mig_create_server(job_t j, cmd_t server_cmd, uid_t server_uid, boolean_t on_ job_log(j, LOG_WARNING, "Server create: \"%s\": As UID %d, we will not be able to switch to UID %d", server_cmd, getuid(), server_uid); } - server_uid = 0; /* zero means "do nothing" */ + server_uid = 0; // zero means "do nothing" } js = job_new_via_mach_init(j, server_cmd, server_uid, on_demand); @@ -7670,18 +7540,18 @@ job_mig_send_signal(job_t j, mach_port_t srp, name_t targetlabel, int sig) struct ldcred *ldc = runtime_get_caller_creds(); job_t otherj; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } if (unlikely(ldc->euid != 0 && ldc->euid != getuid()) || j->deny_job_creation) { - #if TARGET_OS_EMBEDDED - if (!j->embedded_special_privileges) { +#if TARGET_OS_EMBEDDED + if (!j->embedded_god) { return BOOTSTRAP_NOT_PRIVILEGED; } - #else +#else return BOOTSTRAP_NOT_PRIVILEGED; - #endif +#endif } #if HAVE_SANDBOX @@ -7689,14 +7559,20 @@ job_mig_send_signal(job_t j, mach_port_t srp, name_t targetlabel, int sig) return BOOTSTRAP_NOT_PRIVILEGED; } #endif - + if (unlikely(!(otherj = job_find(NULL, targetlabel)))) { return BOOTSTRAP_UNKNOWN_SERVICE; } #if TARGET_OS_EMBEDDED - if (j->embedded_special_privileges && strcmp(j->username, otherj->username) != 0) { - return BOOTSTRAP_NOT_PRIVILEGED; + if (j->embedded_god) { + if (j->username && otherj->username) { + if (strcmp(j->username, otherj->username) != 0) { + return BOOTSTRAP_NOT_PRIVILEGED; + } + } else { + return BOOTSTRAP_NOT_PRIVILEGED; + } } #endif @@ -7711,34 +7587,14 @@ job_mig_send_signal(job_t j, mach_port_t srp, name_t targetlabel, int sig) if (do_block) { job_log(j, LOG_DEBUG, "Blocking MIG return of job_remove(): %s", otherj->label); - /* this is messy. We shouldn't access 'otherj' after job_remove(), but we check otherj->p first... */ + // this is messy. We shouldn't access 'otherj' after job_remove(), but we check otherj->p first... (void)job_assumes(otherj, waiting4removal_new(otherj, srp)); return MIG_NO_REPLY; } else { return 0; } - } else if (sig == VPROC_MAGIC_TRYKILL_SIGNAL) { - if (!j->kill_via_shmem) { - return BOOTSTRAP_NOT_PRIVILEGED; - } - - if (!j->shmem) { - j->sent_kill_via_shmem = true; - (void)job_assumes(j, runtime_kill(otherj->p, SIGKILL) != -1); - return 0; - } - -#if !TARGET_OS_EMBEDDED - if (__sync_bool_compare_and_swap(&j->shmem->vp_shmem_transaction_cnt, 0, -1)) { - j->shmem->vp_shmem_flags |= VPROC_SHMEM_EXITING; - j->sent_kill_via_shmem = true; - (void)job_assumes(j, runtime_kill(otherj->p, SIGKILL) != -1); - return 0; - } -#endif - return BOOTSTRAP_NOT_PRIVILEGED; } else if (otherj->p) { - (void)job_assumes(j, runtime_kill(otherj->p, sig) != -1); + (void)job_assumes_zero_p(j, kill2(otherj->p, sig)); } return 0; @@ -7749,7 +7605,7 @@ job_mig_log_forward(job_t j, vm_offset_t inval, mach_msg_type_number_t invalCnt) { struct ldcred *ldc = runtime_get_caller_creds(); - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -7757,7 +7613,7 @@ job_mig_log_forward(job_t j, vm_offset_t inval, mach_msg_type_number_t invalCnt) return BOOTSTRAP_NOT_PRIVILEGED; } - return runtime_log_forward(ldc->euid, ldc->egid, inval, invalCnt); + return launchd_log_forward(ldc->euid, ldc->egid, inval, invalCnt); } kern_return_t @@ -7765,7 +7621,7 @@ job_mig_log_drain(job_t j, mach_port_t srp, vm_offset_t *outval, mach_msg_type_n { struct ldcred *ldc = runtime_get_caller_creds(); - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -7773,11 +7629,13 @@ job_mig_log_drain(job_t j, mach_port_t srp, vm_offset_t *outval, mach_msg_type_n return BOOTSTRAP_NOT_PRIVILEGED; } - return runtime_log_drain(srp, outval, outvalCnt); + return launchd_log_drain(srp, outval, outvalCnt); } kern_return_t -job_mig_swap_complex(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, vm_offset_t inval, mach_msg_type_number_t invalCnt, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt) +job_mig_swap_complex(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, + vm_offset_t inval, mach_msg_type_number_t invalCnt, vm_offset_t *outval, + mach_msg_type_number_t *outvalCnt) { const char *action; launch_data_t input_obj = NULL, output_obj = NULL; @@ -7785,12 +7643,16 @@ job_mig_swap_complex(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, vm_offset_t size_t packed_size; struct ldcred *ldc = runtime_get_caller_creds(); - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } - if (unlikely(inkey && ldc->euid && ldc->euid != getuid())) { - return BOOTSTRAP_NOT_PRIVILEGED; - } + + if (inkey && ldc->pid != j->p) { + if (ldc->euid && ldc->euid != getuid()) { + return BOOTSTRAP_NOT_PRIVILEGED; + } + } + if (unlikely(inkey && outkey && !job_assumes(j, inkey == outkey))) { return 1; } @@ -7811,14 +7673,16 @@ job_mig_swap_complex(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, vm_offset_t return 1; } - /* Note to future maintainers: launch_data_unpack() does NOT return a heap object. The data - * is decoded in-place. So do not call launch_data_free() on input_obj. + /* Note to future maintainers: launch_data_unpack() does NOT return a heap + * object. The data is decoded in-place. So do not call launch_data_free() + * on input_obj. */ runtime_ktrace0(RTKT_LAUNCHD_DATA_UNPACK); if (unlikely(invalCnt && !job_assumes(j, (input_obj = launch_data_unpack((void *)inval, invalCnt, NULL, 0, &data_offset, NULL)) != NULL))) { goto out_bad; } + char *store = NULL; switch (outkey) { case VPROC_GSK_ENVIRONMENT: if (!job_assumes(j, (output_obj = launch_data_alloc(LAUNCH_DATA_DICTIONARY)))) { @@ -7851,34 +7715,25 @@ job_mig_swap_complex(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, vm_offset_t if (!job_assumes(j, packed_size != 0)) { goto out_bad; } - + launch_data_free(output_obj); break; case VPROC_GSK_JOB_OVERRIDES_DB: - if (!job_assumes(j, (output_obj = launch_data_new_string(launchd_data_base_path(LAUNCHD_DB_TYPE_OVERRIDES))) != NULL)) { - goto out_bad; - } - packed_size = launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL); - if (!job_assumes(j, packed_size != 0)) { - goto out_bad; - } - - launch_data_free(output_obj); - break; - case VPROC_GSK_JOB_CACHE_DB: - if (!job_assumes(j, (output_obj = launch_data_new_string(launchd_data_base_path(LAUNCHD_DB_TYPE_JOBCACHE))) != NULL)) { + store = launchd_copy_persistent_store(LAUNCHD_PERSISTENT_STORE_DB, "overrides.plist"); + if (!store || !job_assumes(j, (output_obj = launch_data_new_string(store)) != NULL)) { + free(store); goto out_bad; } + + free(store); packed_size = launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL); if (!job_assumes(j, packed_size != 0)) { goto out_bad; } - - job_log(j, LOG_DEBUG, "Location of job cache database: %s", launch_data_get_string(output_obj)); - + launch_data_free(output_obj); break; - case 0: + case VPROC_GSK_ZERO: mig_deallocate(*outval, *outvalCnt); *outval = 0; *outvalCnt = 0; @@ -7887,24 +7742,9 @@ job_mig_swap_complex(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, vm_offset_t goto out_bad; } - if (invalCnt) switch (inkey) { - case VPROC_GSK_ENVIRONMENT: - if (launch_data_get_type(input_obj) == LAUNCH_DATA_DICTIONARY) { - if (j->p) { - job_log(j, LOG_INFO, "Setting environment for a currently active job. This environment will take effect on the next invocation of the job."); - } - launch_data_dict_iterate(input_obj, envitem_setup_one_shot, j); - } - break; - case 0: - break; - default: - goto out_bad; - } - mig_deallocate(inval, invalCnt); return 0; - + out_bad: mig_deallocate(inval, invalCnt); if (*outval) { @@ -7913,7 +7753,7 @@ out_bad: if (output_obj) { launch_data_free(output_obj); } - + return 1; } @@ -7925,13 +7765,15 @@ job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inv struct ldcred *ldc = runtime_get_caller_creds(); int oldmask; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } - if (unlikely(inkey && ldc->euid && ldc->euid != getuid())) { - return BOOTSTRAP_NOT_PRIVILEGED; - } + if (inkey && ldc->pid != j->p) { + if (ldc->euid && ldc->euid != getuid()) { + return BOOTSTRAP_NOT_PRIVILEGED; + } + } if (unlikely(inkey && outkey && !job_assumes(j, inkey == outkey))) { return 1; @@ -7986,16 +7828,16 @@ job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inv umask(oldmask); break; case VPROC_GSK_TRANSACTIONS_ENABLED: - job_log(j, LOG_DEBUG, "Reading transaction model status."); - *outval = j->kill_via_shmem; + job_log(j, LOG_DEBUG, "Reading EnableTransactions value."); + *outval = j->enable_transactions; break; case VPROC_GSK_WAITFORDEBUGGER: *outval = j->wait4debugger; break; case VPROC_GSK_EMBEDDEDROOTEQUIVALENT: - *outval = j->embedded_special_privileges; + *outval = j->embedded_god; break; - case 0: + case VPROC_GSK_ZERO: *outval = 0; break; default: @@ -8008,8 +7850,8 @@ job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inv j->abandon_pg = (bool)inval; break; case VPROC_GSK_GLOBAL_ON_DEMAND: - job_log(j, LOG_DEBUG, "Job is setting global on-demand mode to %s (j->forced_peers_to_demand_mode = %s)", (bool)inval ? "true" : "false", j->forced_peers_to_demand_mode ? "true" : "false"); - kr = job_set_global_on_demand(j, (bool)inval) ? 0 : 1; + job_log(j, LOG_DEBUG, "Job has set global on-demand mode to: %s", inval ? "true" : "false"); + kr = job_set_global_on_demand(j, inval); break; case VPROC_GSK_BASIC_KEEPALIVE: j->ondemand = !inval; @@ -8022,9 +7864,9 @@ job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inv runtime_add_weak_ref(); } j->start_interval = (typeof(j->start_interval)) inval; - (void)job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, j->start_interval, j) != -1); + (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, j->start_interval, j)); } else if (j->start_interval) { - (void)job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1); + (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL)); if (j->start_interval != 0) { runtime_del_weak_ref(); } @@ -8053,7 +7895,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: - launchd_assert(sizeof (mode_t) == 2); + __OSX_COMPILETIME_ASSERT__(sizeof (mode_t) == 2); if (inval < 0 || inval > UINT16_MAX) { kr = 1; } else { @@ -8067,24 +7909,19 @@ job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inv } break; case VPROC_GSK_TRANSACTIONS_ENABLED: - if (!job_assumes(j, inval != 0)) { - job_log(j, LOG_WARNING, "Attempt to unregister from transaction model. This is not supported."); - kr = 1; - } else { - j->kill_via_shmem = (bool)inval; - } + /* No-op. */ break; case VPROC_GSK_WEIRD_BOOTSTRAP: if (job_assumes(j, j->weird_bootstrap)) { job_log(j, LOG_DEBUG, "Unsetting weird bootstrap."); - - mach_msg_size_t mxmsgsz = (typeof(mxmsgsz)) sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem); - - if (job_mig_protocol_vproc_subsystem.maxsize > mxmsgsz) { - mxmsgsz = job_mig_protocol_vproc_subsystem.maxsize; - } - - (void)job_assumes(j, runtime_add_mport(j->mgr->jm_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS); + + mach_msg_size_t mxmsgsz = (typeof(mxmsgsz)) sizeof(union __RequestUnion__job_mig_job_subsystem); + + if (job_mig_job_subsystem.maxsize > mxmsgsz) { + mxmsgsz = job_mig_job_subsystem.maxsize; + } + + (void)job_assumes_zero(j, runtime_add_mport(j->mgr->jm_port, job_server)); j->weird_bootstrap = false; } break; @@ -8141,7 +7978,7 @@ job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inv break; } } - + if (!job_assumes(j, spi != NULL)) { job_log(j, LOG_WARNING, "Job tried to resume per-user launchd for UID %lli that it did not suspend.", inval); kr = BOOTSTRAP_NOT_PRIVILEGED; @@ -8154,7 +7991,7 @@ job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inv kr = 1; } break; - case 0: + case VPROC_GSK_ZERO: break; default: kr = 1; @@ -8169,7 +8006,7 @@ job_mig_post_fork_ping(job_t j, task_t child_task, mach_port_t *asport) { struct machservice *ms; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -8179,17 +8016,20 @@ job_mig_post_fork_ping(job_t j, task_t child_task, mach_port_t *asport) 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. */ + // The TASK_ACCESS_PORT funny business is to workaround 5325399. continue; } errno = task_set_special_port(child_task, ms->special_port_num, ms->port); + if (errno) { + if (errno == MACH_SEND_INVALID_DEST) { + job_log(j, LOG_WARNING, "Task died before special ports could be set."); + break; + } - if (unlikely(errno)) { int desired_log_level = LOG_ERR; - if (j->anonymous) { - /* 5338127 */ + // 5338127 desired_log_level = LOG_WARNING; @@ -8212,12 +8052,12 @@ job_mig_post_fork_ping(job_t j, task_t child_task, mach_port_t *asport) * xpcproxy will switch them away if needed. */ if (!(j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) { - job_log(j, LOG_DEBUG, "Returning j->asport: %u", j->asport); + job_log(j, LOG_DEBUG, "Returning session port: 0x%x", j->asport); *asport = j->asport; } } #endif - (void)job_assumes(j, launchd_mport_deallocate(child_task) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_deallocate(child_task)); return 0; } @@ -8230,7 +8070,7 @@ job_mig_reboot2(job_t j, uint64_t flags) struct ldcred *ldc = runtime_get_caller_creds(); pid_t pid_to_log; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -8241,7 +8081,7 @@ job_mig_reboot2(job_t j, uint64_t flags) #if !TARGET_OS_EMBEDDED if (unlikely(ldc->euid)) { #else - if (unlikely(ldc->euid) && !j->embedded_special_privileges) { + if (unlikely(ldc->euid) && !j->embedded_god) { #endif return BOOTSTRAP_NOT_PRIVILEGED; } @@ -8250,7 +8090,7 @@ job_mig_reboot2(job_t j, uint64_t flags) size_t who_offset; if (proc_pidinfo(pid_to_log, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) { if (errno != ESRCH) { - job_assumes(j, errno == 0); + (void)job_assumes_zero(j, errno); } return 1; } @@ -8276,7 +8116,7 @@ job_mig_reboot2(job_t j, uint64_t flags) kern_return_t job_mig_getsocket(job_t j, name_t spr) { - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -8298,14 +8138,14 @@ job_mig_getsocket(job_t j, name_t spr) } strncpy(spr, sockpath, sizeof(name_t)); - + return BOOTSTRAP_SUCCESS; } kern_return_t job_mig_log(job_t j, int pri, int err, logmsg_t msg) { - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -8318,6 +8158,55 @@ job_mig_log(job_t j, int pri, int err, logmsg_t msg) return 0; } +void +job_setup_per_user_directory(job_t j, uid_t uid, const char *path) +{ + struct stat sb; + + bool created = false; + int r = stat(path, &sb); + if ((r == -1 && errno == ENOENT) || (r == 0 && !S_ISDIR(sb.st_mode))) { + if (r == 0) { + job_log(j, LOG_NOTICE, "File at location of per-user launchd directory is not a directory. Moving aside: %s", path); + + char old[PATH_MAX]; + snprintf(old, sizeof(old), "%s.movedaside", path); + (void)job_assumes_zero_p(j, rename(path, old)); + } + + (void)job_assumes_zero_p(j, mkdir(path, S_IRWXU)); + (void)job_assumes_zero_p(j, chown(path, uid, 0)); + created = true; + } + + if (!created) { + if (sb.st_uid != uid) { + job_log(j, LOG_NOTICE, "Per-user launchd directory has improper user ownership. Repairing: %s", path); + (void)job_assumes_zero_p(j, chown(path, uid, 0)); + } + if (sb.st_gid != 0) { + job_log(j, LOG_NOTICE, "Per-user launchd directory has improper group ownership. Repairing: %s", path); + (void)job_assumes_zero_p(j, chown(path, uid, 0)); + } + if (sb.st_mode != (S_IRWXU | S_IFDIR)) { + job_log(j, LOG_NOTICE, "Per-user launchd directory has improper mode. Repairing: %s", path); + (void)job_assumes_zero_p(j, chmod(path, S_IRWXU)); + } + } +} + +void +job_setup_per_user_directories(job_t j, uid_t uid, const char *label) +{ + char path[PATH_MAX]; + + (void)snprintf(path, sizeof(path), LAUNCHD_DB_PREFIX "/%s", label); + job_setup_per_user_directory(j, uid, path); + + (void)snprintf(path, sizeof(path), LAUNCHD_LOG_PREFIX "/%s", label); + job_setup_per_user_directory(j, uid, path); +} + job_t jobmgr_lookup_per_user_context_internal(job_t j, uid_t which_user, mach_port_t *mp) { @@ -8337,20 +8226,22 @@ jobmgr_lookup_per_user_context_internal(job_t j, uid_t which_user, mach_port_t * } break; } - + if (unlikely(ji == NULL)) { struct machservice *ms; char lbuf[1024]; - + job_log(j, LOG_DEBUG, "Creating per user launchd job for UID: %u", which_user); - + sprintf(lbuf, "com.apple.launchd.peruser.%u", which_user); - + ji = job_new(root_jobmgr, lbuf, "/sbin/launchd", NULL); - + if (ji != NULL) { auditinfo_addr_t auinfo = { - .ai_termid = { .at_type = AU_IPv4 }, + .ai_termid = { + .at_type = AU_IPv4 + }, .ai_auid = which_user, .ai_asid = AU_ASSIGN_ASID, }; @@ -8362,7 +8253,7 @@ jobmgr_lookup_per_user_context_internal(job_t j, uid_t which_user, mach_port_t * /* Kinda lame that we have to do this, but we can't create an * audit session without joining it. */ - (void)job_assumes(ji, audit_session_join(g_audit_session_port)); + (void)job_assumes(ji, audit_session_join(launchd_audit_port)); ji->asid = auinfo.ai_asid; } else { job_log(ji, LOG_WARNING, "Could not set audit session!"); @@ -8372,43 +8263,14 @@ jobmgr_lookup_per_user_context_internal(job_t j, uid_t which_user, mach_port_t * ji->mach_uid = which_user; ji->per_user = true; - ji->kill_via_shmem = true; - - struct stat sb; - char pu_db[PATH_MAX]; - snprintf(pu_db, sizeof(pu_db), LAUNCHD_DB_PREFIX "/%s", lbuf); - - bool created = false; - int err = stat(pu_db, &sb); - if ((err == -1 && errno == ENOENT) || (err == 0 && !S_ISDIR(sb.st_mode))) { - if (err == 0) { - char move_aside[PATH_MAX]; - snprintf(move_aside, sizeof(move_aside), LAUNCHD_DB_PREFIX "/%s.movedaside", lbuf); - - (void)job_assumes(ji, rename(pu_db, move_aside) != -1); - } + ji->enable_transactions = true; + job_setup_per_user_directories(ji, which_user, lbuf); - (void)job_assumes(ji, mkdir(pu_db, S_IRWXU) != -1); - (void)job_assumes(ji, chown(pu_db, which_user, 0) != -1); - created = true; - } - - if (!created) { - if (!job_assumes(ji, sb.st_uid == which_user)) { - (void)job_assumes(ji, chown(pu_db, which_user, 0) != -1); - } - if (!job_assumes(ji, sb.st_gid == 0)) { - (void)job_assumes(ji, chown(pu_db, which_user, 0) != -1); - } - if (!job_assumes(ji, sb.st_mode == (S_IRWXU | S_IFDIR))) { - (void)job_assumes(ji, chmod(pu_db, S_IRWXU) != -1); - } - } - if ((ms = machservice_new(ji, lbuf, mp, false)) == NULL) { job_remove(ji); ji = NULL; } else { + ms->upfront = true; ms->per_user_hack = true; ms->hide = true; @@ -8419,7 +8281,7 @@ jobmgr_lookup_per_user_context_internal(job_t j, uid_t which_user, mach_port_t * *mp = machservice_port(SLIST_FIRST(&ji->machservices)); job_log(j, LOG_DEBUG, "Per user launchd job found for UID: %u", which_user); } - + return ji; } @@ -8428,38 +8290,42 @@ job_mig_lookup_per_user_context(job_t j, uid_t which_user, mach_port_t *up_cont) { struct ldcred *ldc = runtime_get_caller_creds(); job_t jpu; - + + if (!j) { + return BOOTSTRAP_NO_MEMORY; + } + + if (launchd_osinstaller) { + return BOOTSTRAP_UNKNOWN_SERVICE; + } + #if TARGET_OS_EMBEDDED - /* There is no need for per-user launchd's on embedded. */ + // There is no need for per-user launchd's on embedded. job_log(j, LOG_ERR, "Per-user launchds are not supported on this platform."); - return BOOTSTRAP_NOT_PRIVILEGED; + return BOOTSTRAP_UNKNOWN_SERVICE; #endif - + #if HAVE_SANDBOX if (unlikely(sandbox_check(ldc->pid, "mach-per-user-lookup", SANDBOX_FILTER_NONE) > 0)) { return BOOTSTRAP_NOT_PRIVILEGED; } #endif - - if (!launchd_assumes(j != NULL)) { - return BOOTSTRAP_NO_MEMORY; - } - + job_log(j, LOG_INFO, "Looking up per user launchd for UID: %u", which_user); - + if (unlikely(!pid1_magic)) { job_log(j, LOG_ERR, "Only PID 1 supports per user launchd lookups."); return BOOTSTRAP_NOT_PRIVILEGED; } - + if (ldc->euid || ldc->uid) { which_user = ldc->euid ?: ldc->uid; } - + *up_cont = MACH_PORT_NULL; - + jpu = jobmgr_lookup_per_user_context_internal(j, which_user, up_cont); - + return 0; } @@ -8472,7 +8338,7 @@ job_mig_check_in2(job_t j, name_t servicename, mach_port_t *serviceportp, uuid_t struct machservice *ms = NULL; job_t jo; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -8502,19 +8368,19 @@ job_mig_check_in2(job_t j, name_t servicename, mach_port_t *serviceportp, uuid_t } else if (ms == NULL) { if (job_assumes(j, !j->dedicated_instance)) { *serviceportp = MACH_PORT_NULL; - + if (unlikely((ms = machservice_new(j, servicename, serviceportp, per_pid_service)) == NULL)) { return BOOTSTRAP_NO_MEMORY; } - - /* Treat this like a legacy job. */ + + // Treat this like a legacy job. if (!j->legacy_mach_job) { ms->isActive = true; ms->recv = false; } - + if (!(j->anonymous || j->legacy_LS_job || j->legacy_mach_job)) { - job_log(j, LOG_SCOLDING, "Please add the following service to the configuration file for this job: %s", servicename); + job_log(j, LOG_APPLEONLY, "Please add the following service to the configuration file for this job: %s", servicename); } } else { return BOOTSTRAP_UNKNOWN_SERVICE; @@ -8551,17 +8417,17 @@ 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(); - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } if (!(flags & BOOTSTRAP_PER_PID_SERVICE) && !j->legacy_LS_job) { - job_log(j, LOG_SCOLDING, "Performance: bootstrap_register() is deprecated. Service: %s", servicename); + 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); - /* 5641783 for the embedded hack */ + // 5641783 for the embedded hack #if !TARGET_OS_EMBEDDED /* * From a per-user/session launchd's perspective, SecurityAgent (UID @@ -8576,7 +8442,7 @@ job_mig_register2(job_t j, name_t servicename, mach_port_t serviceport, uint64_t } } #endif - + ms = jobmgr_lookup_service(j->mgr, servicename, false, flags & BOOTSTRAP_PER_PID_SERVICE ? ldc->pid : 0); if (unlikely(ms)) { @@ -8603,7 +8469,7 @@ job_mig_register2(job_t j, name_t servicename, mach_port_t serviceport, uint64_t } } - + return BOOTSTRAP_SUCCESS; } @@ -8618,13 +8484,13 @@ job_mig_look_up2(job_t j, mach_port_t srp, name_t servicename, mach_port_t *serv bool strict_lookup = flags & BOOTSTRAP_STRICT_LOOKUP; bool privileged = flags & BOOTSTRAP_PRIVILEGED_SERVER; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } - bool xpc_req = j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN; + bool xpc_req = (j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN); - /* 5641783 for the embedded hack */ + // 5641783 for the embedded hack #if !TARGET_OS_EMBEDDED if (unlikely(pid1_magic && j->anonymous && j->mgr->parentmgr == NULL && ldc->uid != 0 && ldc->euid != 0)) { return VPROC_ERR_TRY_PER_USER; @@ -8633,7 +8499,7 @@ job_mig_look_up2(job_t j, mach_port_t srp, name_t servicename, mach_port_t *serv #if HAVE_SANDBOX /* We don't do sandbox checking for XPC domains because, by definition, all - * the services within your domain should be accessibly to you. + * the services within your domain should be accessible to you. */ if (!xpc_req && unlikely(sandbox_check(ldc->pid, "mach-lookup", per_pid_lookup ? SANDBOX_FILTER_LOCAL_NAME : SANDBOX_FILTER_GLOBAL_NAME, servicename) > 0)) { return BOOTSTRAP_NOT_PRIVILEGED; @@ -8644,7 +8510,7 @@ job_mig_look_up2(job_t j, mach_port_t srp, name_t servicename, mach_port_t *serv ms = jobmgr_lookup_service(j->mgr, servicename, false, target_pid); } else { if (xpc_req) { - /* Requests from XPC domains stay local. */ + // Requests from XPC domains stay local. ms = jobmgr_lookup_service(j->mgr, servicename, false, 0); } else { /* A strict lookup which is privileged won't even bother trying to @@ -8692,14 +8558,15 @@ job_mig_look_up2(job_t j, mach_port_t srp, name_t servicename, mach_port_t *serv job_dispatch(instance, false); } } - + ms = NULL; if (job_assumes(j, instance != NULL)) { struct machservice *msi = NULL; SLIST_FOREACH(msi, &instance->machservices, sle) { - /* sizeof(servicename) will return the size of a pointer, even though it's - * an array type, because when passing arrays as parameters in C, they - * implicitly degrade to pointers. + /* sizeof(servicename) will return the size of a pointer, + * even though it's an array type, because when passing + * arrays as parameters in C, they implicitly degrade to + * pointers. */ if (strncmp(servicename, msi->name, sizeof(name_t) - 1) == 0) { ms = msi; @@ -8719,38 +8586,31 @@ job_mig_look_up2(job_t j, mach_port_t srp, name_t servicename, mach_port_t *serv if (likely(ms)) { (void)job_assumes(j, machservice_port(ms) != MACH_PORT_NULL); job_log(j, LOG_DEBUG, "%sMach service lookup: %s", per_pid_lookup ? "Per PID " : "", servicename); - - if (unlikely(!per_pid_lookup && j->lastlookup == ms && j->lastlookup_gennum == ms->gen_num && !j->per_user)) { - /* we need to think more about the per_pid_lookup logic before logging about repeated lookups */ - job_log(j, LOG_DEBUG, "Performance: Please fix the framework that talks to \"%s\" to cache the Mach port for service: %s", ms->job->label, servicename); - } - - j->lastlookup = ms; - j->lastlookup_gennum = ms->gen_num; - *serviceportp = machservice_port(ms); kr = BOOTSTRAP_SUCCESS; } else if (strict_lookup && !privileged) { - /* Hack: We need to simulate XPC's desire not to establish a hierarchy. So if - * XPC is doing the lookup, and it's not a privileged lookup, we won't forward. - * But if it is a privileged lookup (that is, was looked up in XPC_DOMAIN_LOCAL_SYSTEM) - * then we must forward. + /* Hack: We need to simulate XPC's desire not to establish a hierarchy. + * So if XPC is doing the lookup, and it's not a privileged lookup, we + * won't forward. But if it is a privileged lookup, then we must + * forward. */ return BOOTSTRAP_UNKNOWN_SERVICE; } else if (inherited_bootstrap_port != MACH_PORT_NULL) { - /* Requests from within an XPC domain don't get forwarded. */ + // Requests from within an XPC domain don't get forwarded. job_log(j, LOG_DEBUG, "Mach service lookup forwarded: %s", servicename); - /* Clients potentially check the audit token of the reply to verify that the returned send right is trustworthy. */ - (void)job_assumes(j, vproc_mig_look_up2_forward(inherited_bootstrap_port, srp, servicename, target_pid, instance_id, flags) == 0); - /* The previous routine moved the reply port, we're forced to return MIG_NO_REPLY now */ + /* Clients potentially check the audit token of the reply to verify that + * the returned send right is trustworthy. + */ + (void)job_assumes_zero(j, vproc_mig_look_up2_forward(inherited_bootstrap_port, srp, servicename, target_pid, instance_id, flags)); return MIG_NO_REPLY; } else if (pid1_magic && j->anonymous && ldc->euid >= 500 && strcasecmp(j->mgr->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) { - /* - * 5240036 Should start background session when a lookup of CCacheServer occurs + /* 5240036 Should start background session when a lookup of CCacheServer + * occurs * - * This is a total hack. We sniff out loginwindow session, and attempt to guess what it is up to. - * If we find a EUID that isn't root, we force it over to the per-user context. + * This is a total hack. We sniff out loginwindow session, and attempt + * to guess what it is up to. If we find a EUID that isn't root, we + * force it over to the per-user context. */ return VPROC_ERR_TRY_PER_USER; } else { @@ -8764,7 +8624,7 @@ job_mig_look_up2(job_t j, mach_port_t srp, name_t servicename, mach_port_t *serv kern_return_t job_mig_parent(job_t j, mach_port_t srp, mach_port_t *parentport) { - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -8776,8 +8636,8 @@ job_mig_parent(job_t j, mach_port_t srp, mach_port_t *parentport) } else if (MACH_PORT_NULL == inherited_bootstrap_port) { *parentport = jm->jm_port; } else { - (void)job_assumes(j, vproc_mig_parent_forward(inherited_bootstrap_port, srp) == 0); - /* The previous routine moved the reply port, we're forced to return MIG_NO_REPLY now */ + (void)job_assumes_zero(j, vproc_mig_parent_forward(inherited_bootstrap_port, srp)); + // The previous routine moved the reply port, we're forced to return MIG_NO_REPLY now return MIG_NO_REPLY; } return BOOTSTRAP_SUCCESS; @@ -8792,17 +8652,20 @@ job_mig_get_root_bootstrap(job_t j, mach_port_t *rootbsp) if (inherited_bootstrap_port == MACH_PORT_NULL) { *rootbsp = root_jobmgr->jm_port; - (void)job_assumes(j, launchd_mport_make_send(root_jobmgr->jm_port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_make_send(root_jobmgr->jm_port)); } else { *rootbsp = inherited_bootstrap_port; - (void)job_assumes(j, launchd_mport_copy_send(inherited_bootstrap_port) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_copy_send(inherited_bootstrap_port)); } return BOOTSTRAP_SUCCESS; } kern_return_t -job_mig_info(job_t j, name_array_t *servicenamesp, unsigned int *servicenames_cnt, name_array_t *servicejobsp, unsigned int *servicejobs_cnt, bootstrap_status_array_t *serviceactivesp, unsigned int *serviceactives_cnt, uint64_t flags) +job_mig_info(job_t j, name_array_t *servicenamesp, + unsigned int *servicenames_cnt, name_array_t *servicejobsp, + unsigned int *servicejobs_cnt, bootstrap_status_array_t *serviceactivesp, + unsigned int *serviceactives_cnt, uint64_t flags) { name_array_t service_names = NULL; name_array_t service_jobs = NULL; @@ -8810,11 +8673,11 @@ job_mig_info(job_t j, name_array_t *servicenamesp, unsigned int *servicenames_cn unsigned int cnt = 0, cnt2 = 0; jobmgr_t jm; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } - if (g_flat_mach_namespace) { + if (launchd_flat_mach_namespace) { if ((j->mgr->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET) || (flags & BOOTSTRAP_FORCE_LOCAL)) { jm = j->mgr; } else { @@ -8892,15 +8755,19 @@ out_bad: } kern_return_t -job_mig_lookup_children(job_t j, mach_port_array_t *child_ports, mach_msg_type_number_t *child_ports_cnt, name_array_t *child_names, mach_msg_type_number_t *child_names_cnt, bootstrap_property_array_t *child_properties,mach_msg_type_number_t *child_properties_cnt) +job_mig_lookup_children(job_t j, mach_port_array_t *child_ports, + mach_msg_type_number_t *child_ports_cnt, name_array_t *child_names, + mach_msg_type_number_t *child_names_cnt, + bootstrap_property_array_t *child_properties, + mach_msg_type_number_t *child_properties_cnt) { kern_return_t kr = BOOTSTRAP_NO_MEMORY; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } - + struct ldcred *ldc = runtime_get_caller_creds(); - + /* Only allow root processes to look up children, even if we're in the per-user launchd. * Otherwise, this could be used to cross sessions, which counts as a security vulnerability * in a non-flat namespace. @@ -8909,68 +8776,68 @@ job_mig_lookup_children(job_t j, mach_port_array_t *child_ports, mach_msg_type_n job_log(j, LOG_WARNING, "Attempt to look up children of bootstrap by unprivileged job."); return BOOTSTRAP_NOT_PRIVILEGED; } - + unsigned int cnt = 0; - + jobmgr_t jmr = j->mgr; jobmgr_t jmi = NULL; SLIST_FOREACH(jmi, &jmr->submgrs, sle) { cnt++; } - - /* Find our per-user launchds if we're PID 1. */ + + // Find our per-user launchds if we're PID 1. job_t ji = NULL; if (pid1_magic) { LIST_FOREACH(ji, &jmr->jobs, sle) { cnt += ji->per_user ? 1 : 0; } } - + if (cnt == 0) { return BOOTSTRAP_NO_CHILDREN; } - + mach_port_array_t _child_ports = 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; goto out_bad; } - + unsigned int cnt2 = 0; SLIST_FOREACH(jmi, &jmr->submgrs, sle) { - if (jobmgr_assumes(jmi, launchd_mport_make_send(jmi->jm_port) == KERN_SUCCESS)) { + if (jobmgr_assumes_zero(jmi, launchd_mport_make_send(jmi->jm_port)) == KERN_SUCCESS) { _child_ports[cnt2] = jmi->jm_port; } else { _child_ports[cnt2] = MACH_PORT_NULL; } - + strlcpy(_child_names[cnt2], jmi->name, sizeof(_child_names[0])); _child_properties[cnt2] = jmi->properties; - + cnt2++; } - - if (pid1_magic) LIST_FOREACH( ji, &jmr->jobs, sle) { + + if (pid1_magic) LIST_FOREACH(ji, &jmr->jobs, sle) { if (ji->per_user) { if (job_assumes(ji, SLIST_FIRST(&ji->machservices)->per_user_hack == true)) { mach_port_t port = machservice_port(SLIST_FIRST(&ji->machservices)); - - if (job_assumes(ji, launchd_mport_copy_send(port) == KERN_SUCCESS)) { + + if (job_assumes_zero(ji, launchd_mport_copy_send(port)) == KERN_SUCCESS) { _child_ports[cnt2] = port; } else { _child_ports[cnt2] = MACH_PORT_NULL; @@ -8978,88 +8845,52 @@ job_mig_lookup_children(job_t j, mach_port_array_t *child_ports, mach_msg_type_n } else { _child_ports[cnt2] = MACH_PORT_NULL; } - + strlcpy(_child_names[cnt2], ji->label, sizeof(_child_names[0])); _child_properties[cnt2] |= BOOTSTRAP_PROPERTY_PERUSER; - + cnt2++; } } - + *child_names_cnt = cnt; *child_ports_cnt = cnt; *child_properties_cnt = cnt; - + *child_names = _child_names; *child_ports = _child_ports; *child_properties = _child_properties; - + unsigned int i = 0; for (i = 0; i < cnt; i++) { job_log(j, LOG_DEBUG, "child_names[%u] = %s", i, (char *)_child_names[i]); } - + return BOOTSTRAP_SUCCESS; out_bad: if (_child_ports) { mig_deallocate((vm_address_t)_child_ports, cnt * sizeof(_child_ports[0])); } - + if (_child_names) { mig_deallocate((vm_address_t)_child_names, cnt * sizeof(_child_ports[0])); } - + if (_child_properties) { mig_deallocate((vm_address_t)_child_properties, cnt * sizeof(_child_properties[0])); } - + return kr; } kern_return_t -job_mig_transaction_count_for_pid(job_t j, pid_t p, int32_t *cnt, boolean_t *condemned) +job_mig_pid_is_managed(job_t j __attribute__((unused)), pid_t p, boolean_t *managed) { - if (!j) { - return BOOTSTRAP_NO_MEMORY; - } - - kern_return_t kr = KERN_FAILURE; struct ldcred *ldc = runtime_get_caller_creds(); if ((ldc->euid != geteuid()) && (ldc->euid != 0)) { return BOOTSTRAP_NOT_PRIVILEGED; } - - job_t j_for_pid = jobmgr_find_by_pid_deep(j->mgr, p, false); - if (j_for_pid) { - if (j_for_pid->kill_via_shmem) { - if (j_for_pid->shmem) { - *cnt = j_for_pid->shmem->vp_shmem_transaction_cnt; - *condemned = j_for_pid->shmem->vp_shmem_flags & VPROC_SHMEM_EXITING; - *cnt += *condemned ? 1 : 0; - } else { - *cnt = 0; - *condemned = false; - } - - kr = BOOTSTRAP_SUCCESS; - } else { - kr = BOOTSTRAP_NO_MEMORY; - } - } else { - kr = BOOTSTRAP_UNKNOWN_SERVICE; - } - - return kr; -} -kern_return_t -job_mig_pid_is_managed(job_t j __attribute__((unused)), pid_t p, boolean_t *managed) -{ - struct ldcred *ldc = runtime_get_caller_creds(); - if ((ldc->euid != geteuid()) && (ldc->euid != 0)) { - return BOOTSTRAP_NOT_PRIVILEGED; - } - /* This is so loginwindow doesn't try to quit GUI apps that have been launched * directly by launchd as agents. */ @@ -9067,7 +8898,7 @@ job_mig_pid_is_managed(job_t j __attribute__((unused)), pid_t p, boolean_t *mana if (j_for_pid && !j_for_pid->anonymous && !j_for_pid->legacy_LS_job) { *managed = true; } - + return BOOTSTRAP_SUCCESS; } @@ -9094,7 +8925,7 @@ job_mig_port_for_label(job_t j __attribute__((unused)), name_t label, mach_port_ if (target_j->j_port == MACH_PORT_NULL) { (void)job_assumes(target_j, job_setup_machport(target_j) == true); } - + _mp = target_j->j_port; kr = _mp != MACH_PORT_NULL ? BOOTSTRAP_SUCCESS : BOOTSTRAP_NO_MEMORY; } else { @@ -9106,10 +8937,13 @@ job_mig_port_for_label(job_t j __attribute__((unused)), name_t label, mach_port_ return kr; } -#if !TARGET_OS_EMBEDDED kern_return_t job_mig_set_security_session(job_t j, uuid_t uuid, mach_port_t asport) { +#if TARGET_OS_EMBEDDED + return KERN_SUCCESS; +#endif + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -9117,7 +8951,7 @@ job_mig_set_security_session(job_t j, uuid_t uuid, mach_port_t asport) uuid_string_t uuid_str; uuid_unparse(uuid, uuid_str); job_log(j, LOG_DEBUG, "Setting session %u for UUID %s...", asport, uuid_str); - + job_t ji = NULL, jt = NULL; LIST_FOREACH_SAFE(ji, &s_needing_sessions, sle, jt) { uuid_string_t uuid_str2; @@ -9125,42 +8959,35 @@ job_mig_set_security_session(job_t j, uuid_t uuid, mach_port_t asport) if (uuid_compare(uuid, ji->expected_audit_uuid) == 0) { uuid_clear(ji->expected_audit_uuid); - if (asport != MACH_PORT_NULL ) { - job_log(ji, LOG_DEBUG, "Job should join session with port %u", asport); - (void)job_assumes(j, launchd_mport_copy_send(asport) == KERN_SUCCESS); + if (asport != MACH_PORT_NULL) { + job_log(ji, LOG_DEBUG, "Job should join session with port 0x%x", asport); + (void)job_assumes_zero(j, launchd_mport_copy_send(asport)); } else { job_log(ji, LOG_DEBUG, "No session to set for job. Using our session."); } - + ji->asport = asport; LIST_REMOVE(ji, needing_session_sle); job_dispatch(ji, false); } } - + /* Each job that the session port was set for holds a reference. At the end of * the loop, there will be one extra reference belonging to this MiG protocol. * We need to release it so that the session goes away when all the jobs * referencing it are unloaded. */ - (void)job_assumes(j, launchd_mport_deallocate(asport) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_deallocate(asport)); return KERN_SUCCESS; } -#else -kern_return_t -job_mig_set_security_session(job_t j __attribute__((unused)), uuid_t uuid __attribute__((unused)), mach_port_t session __attribute__((unused))) -{ - return KERN_SUCCESS; -} -#endif jobmgr_t jobmgr_find_by_name(jobmgr_t jm, const char *where) { jobmgr_t jmi, jmi2; - /* NULL is only passed for our custom API for LaunchServices. If that is the case, we do magic. */ + // NULL is only passed for our custom API for LaunchServices. If that is the case, we do magic. if (where == NULL) { if (strcasecmp(jm->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) { where = VPROCMGR_SESSION_LOGINWINDOW; @@ -9172,7 +8999,7 @@ jobmgr_find_by_name(jobmgr_t jm, const char *where) if (strcasecmp(jm->name, where) == 0) { return jm; } - + if (strcasecmp(where, VPROCMGR_SESSION_BACKGROUND) == 0 && !pid1_magic) { jmi = root_jobmgr; goto jm_found; @@ -9194,7 +9021,7 @@ jobmgr_find_by_name(jobmgr_t jm, const char *where) } } } - + jm_found: return jmi; } @@ -9210,7 +9037,7 @@ job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type, mac struct ldcred *ldc = runtime_get_caller_creds(); jobmgr_t jmr = NULL; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -9224,12 +9051,13 @@ job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type, mac job_log(j, LOG_DEBUG, "Move subset attempt: 0x%x", target_subset); kr = _vproc_grab_subset(target_subset, &reqport, &rcvright, &out_obj_array, &l2l_ports, &l2l_port_cnt); - - if (!job_assumes(j, kr == 0)) { + if (job_assumes_zero(j, kr) != 0) { goto out; } - launchd_assert(launch_data_array_get_count(out_obj_array) == l2l_port_cnt); + if (launch_data_array_get_count(out_obj_array) != l2l_port_cnt) { + osx_assert_zero(l2l_port_cnt); + } if (!job_assumes(j, (jmr = jobmgr_new(j->mgr, reqport, rcvright, false, session_type, false, asport)) != NULL)) { kr = BOOTSTRAP_NO_MEMORY; @@ -9243,24 +9071,25 @@ job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type, mac * requests. This serialization is guaranteed since we are single-threaded in that respect. */ if (flags & LAUNCH_GLOBAL_ON_DEMAND) { - /* This is so awful. */ - /* Remove the job from its current job manager. */ + // This is so awful. + // Remove the job from its current job manager. LIST_REMOVE(j, sle); LIST_REMOVE(j, pid_hash_sle); - /* Put the job into the target job manager. */ + // Put the job into the target job manager. LIST_INSERT_HEAD(&jmr->jobs, j, sle); LIST_INSERT_HEAD(&jmr->active_jobs[ACTIVE_JOB_HASH(j->p)], j, pid_hash_sle); - + j->mgr = jmr; job_set_global_on_demand(j, true); - + if (!j->holds_ref) { + job_log(j, LOG_PERF, "Job moved subset into: %s", j->mgr->name); j->holds_ref = true; runtime_add_ref(); } } - + for (l2l_i = 0; l2l_i < l2l_port_cnt; l2l_i++) { launch_data_t tmp, obj_at_idx; struct machservice *ms; @@ -9280,8 +9109,8 @@ job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type, mac j_for_service = jobmgr_find_by_pid(jmr, target_pid, true); if (unlikely(!j_for_service)) { - /* The PID probably exited */ - (void)job_assumes(j, launchd_mport_deallocate(l2l_ports[l2l_i]) == KERN_SUCCESS); + // The PID probably exited + (void)job_assumes_zero(j, launchd_mport_deallocate(l2l_ports[l2l_i])); continue; } @@ -9304,10 +9133,10 @@ out: if (kr == 0) { if (target_subset) { - (void)job_assumes(j, launchd_mport_deallocate(target_subset) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_deallocate(target_subset)); } if (asport) { - (void)job_assumes(j, launchd_mport_deallocate(asport) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_deallocate(asport)); } } else if (jmr) { jobmgr_shutdown(jmr); @@ -9324,14 +9153,14 @@ job_mig_init_session(job_t j, name_t session_type, mach_port_t asport) } job_t j2; - + kern_return_t kr = BOOTSTRAP_NO_MEMORY; if (j->mgr->session_initialized) { job_log(j, LOG_ERR, "Tried to initialize an already setup session!"); kr = BOOTSTRAP_NOT_PRIVILEGED; } else if (strcmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) { jobmgr_t jmi; - + /* * 5330262 * @@ -9345,7 +9174,7 @@ job_mig_init_session(job_t j, name_t session_type, mach_port_t asport) * previous LoginWindow context is on the way out and therefore we should just * kick-start the shutdown of it. */ - + SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) { if (unlikely(jmi->shutting_down)) { continue; @@ -9355,16 +9184,16 @@ job_mig_init_session(job_t j, name_t session_type, mach_port_t asport) } } } - + jobmgr_log(j->mgr, LOG_DEBUG, "Initializing as %s", session_type); strcpy(j->mgr->name_init, session_type); - + if (job_assumes(j, (j2 = jobmgr_init_session(j->mgr, session_type, false)))) { j2->asport = asport; (void)job_assumes(j, job_dispatch(j2, true)); kr = BOOTSTRAP_SUCCESS; } - + return kr; } @@ -9382,38 +9211,40 @@ job_mig_switch_to_session(job_t j, mach_port_t requestor_port, name_t session_na } job_log(j, LOG_DEBUG, "Job wants to move to %s session.", session_name); - + if (!job_assumes(j, pid1_magic == false)) { job_log(j, LOG_WARNING, "Switching sessions is not allowed in the system Mach bootstrap."); return BOOTSTRAP_NOT_PRIVILEGED; } - + if (!j->anonymous) { job_log(j, LOG_NOTICE, "Non-anonymous job tried to switch sessions. Please use LimitLoadToSessionType instead."); return BOOTSTRAP_NOT_PRIVILEGED; } - + jobmgr_t target_jm = jobmgr_find_by_name(root_jobmgr, session_name); if (target_jm == j->mgr) { job_log(j, LOG_DEBUG, "Job is already in its desired session (%s).", session_name); + (void)job_assumes_zero(j, launchd_mport_deallocate(asport)); + (void)job_assumes_zero(j, launchd_mport_deallocate(requestor_port)); *new_bsport = target_jm->jm_port; return BOOTSTRAP_SUCCESS; } - + if (!target_jm) { target_jm = jobmgr_new(j->mgr, requestor_port, MACH_PORT_NULL, false, session_name, false, asport); if (target_jm) { target_jm->properties |= BOOTSTRAP_PROPERTY_IMPLICITSUBSET; - (void)job_assumes(j, launchd_mport_deallocate(asport) == KERN_SUCCESS); + (void)job_assumes_zero(j, launchd_mport_deallocate(asport)); } } - + if (!job_assumes(j, target_jm != NULL)) { job_log(j, LOG_WARNING, "Could not find %s session!", session_name); return BOOTSTRAP_NO_MEMORY; } - - /* Remove the job from it's current job manager. */ + + // Remove the job from it's current job manager. LIST_REMOVE(j, sle); LIST_REMOVE(j, pid_hash_sle); @@ -9424,26 +9255,26 @@ job_mig_switch_to_session(job_t j, mach_port_t requestor_port, name_t session_na break; } } - - /* Put the job into the target job manager. */ + + // Put the job into the target job manager. LIST_INSERT_HEAD(&target_jm->jobs, j, sle); LIST_INSERT_HEAD(&target_jm->active_jobs[ACTIVE_JOB_HASH(j->p)], j, pid_hash_sle); - + if (ji) { LIST_INSERT_HEAD(&target_jm->global_env_jobs, j, global_env_sle); } - - /* Move our Mach services over if we're not in a flat namespace. */ - if (!g_flat_mach_namespace && !SLIST_EMPTY(&j->machservices)) { + + // Move our Mach services over if we're not in a flat namespace. + if (!launchd_flat_mach_namespace && !SLIST_EMPTY(&j->machservices)) { struct machservice *msi = NULL, *msit = NULL; SLIST_FOREACH_SAFE(msi, &j->machservices, sle, msit) { LIST_REMOVE(msi, name_hash_sle); LIST_INSERT_HEAD(&target_jm->ms_hash[hash_ms(msi->name)], msi, name_hash_sle); } } - + j->mgr = target_jm; - + if (!j->holds_ref) { /* Anonymous jobs which move around are particularly interesting to us, so we want to * stick around while they're still around. @@ -9452,11 +9283,12 @@ job_mig_switch_to_session(job_t j, mach_port_t requestor_port, name_t session_na * ourselves from going away. */ j->holds_ref = true; + job_log(j, LOG_PERF, "Job switched into manager: %s", j->mgr->name); runtime_add_ref(); } - + *new_bsport = target_jm->jm_port; - + return KERN_SUCCESS; } @@ -9473,7 +9305,7 @@ job_mig_take_subset(job_t j, mach_port_t *reqport, mach_port_t *rcvright, jobmgr_t jm; job_t ji; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -9555,8 +9387,8 @@ job_mig_take_subset(job_t j, mach_port_t *reqport, mach_port_t *rcvright, ports[cnt2] = machservice_port(ms); - /* Increment the send right by one so we can shutdown the jobmgr cleanly */ - (void)jobmgr_assumes(jm, (errno = launchd_mport_copy_send(ports[cnt2])) == KERN_SUCCESS); + // Increment the send right by one so we can shutdown the jobmgr cleanly + (void)jobmgr_assumes_zero(jm, launchd_mport_copy_send(ports[cnt2])); cnt2++; } } @@ -9606,7 +9438,7 @@ job_mig_subset(job_t j, mach_port_t requestorport, mach_port_t *subsetportp) int bsdepth = 0; jobmgr_t jmr; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } if (j->mgr->shutting_down) { @@ -9619,7 +9451,7 @@ job_mig_subset(job_t j, mach_port_t requestorport, mach_port_t *subsetportp) bsdepth++; } - /* Since we use recursion, we need an artificial depth for subsets */ + // Since we use recursion, we need an artificial depth for subsets if (unlikely(bsdepth > 100)) { job_log(j, LOG_ERR, "Mach sub-bootstrap create request failed. Depth greater than: %d", bsdepth); return BOOTSTRAP_NO_MEMORY; @@ -9637,33 +9469,50 @@ job_mig_subset(job_t j, mach_port_t requestorport, mach_port_t *subsetportp) *subsetportp = jmr->jm_port; jmr->properties |= BOOTSTRAP_PROPERTY_EXPLICITSUBSET; - + /* A job could create multiple subsets, so only add a reference the first time * it does so we don't have to keep a count. */ if (j->anonymous && !j->holds_ref) { + job_log(j, LOG_PERF, "Job created subset: %s", jmr->name); j->holds_ref = true; runtime_add_ref(); } - + job_log(j, LOG_DEBUG, "Job created a subset named \"%s\"", jmr->name); return BOOTSTRAP_SUCCESS; } -#ifndef __LAUNCH_DISABLE_XPC_SUPPORT__ job_t -xpc_domain_import_service(jobmgr_t jm, launch_data_t pload) +_xpc_domain_import_service(jobmgr_t jm, launch_data_t pload) { jobmgr_t where2put = NULL; + if (launch_data_get_type(pload) != LAUNCH_DATA_DICTIONARY) { + errno = EINVAL; + return NULL; + } + + launch_data_t ldlabel = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_LABEL); + if (!ldlabel || launch_data_get_type(ldlabel) != LAUNCH_DATA_STRING) { + errno = EINVAL; + return NULL; + } + + const char *label = launch_data_get_string(ldlabel); + jobmgr_log(jm, LOG_DEBUG, "Importing service: %s", label); + launch_data_t destname = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_XPCDOMAIN); if (destname) { + bool supported_domain = false; + if (launch_data_get_type(destname) == LAUNCH_DATA_STRING) { const char *str = launch_data_get_string(destname); if (strcmp(str, XPC_DOMAIN_TYPE_SYSTEM) == 0) { where2put = _s_xpc_system_domain; } else if (strcmp(str, XPC_DOMAIN_TYPE_PERUSER) == 0) { where2put = jobmgr_find_xpc_per_user_domain(jm, jm->req_euid); + supported_domain = true; } else if (strcmp(str, XPC_DOMAIN_TYPE_PERSESSION) == 0) { where2put = jobmgr_find_xpc_per_session_domain(jm, jm->req_asid); } else { @@ -9675,7 +9524,7 @@ xpc_domain_import_service(jobmgr_t jm, launch_data_t pload) errno = EINVAL; } - if (where2put) { + if (where2put && !supported_domain) { launch_data_t mi = NULL; if ((mi = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_MULTIPLEINSTANCES))) { if (launch_data_get_type(mi) == LAUNCH_DATA_BOOL && launch_data_get_bool(mi)) { @@ -9691,40 +9540,114 @@ xpc_domain_import_service(jobmgr_t jm, launch_data_t pload) job_t j = NULL; if (where2put) { + /* Gross. If the service already exists in a singleton domain, then + * jobmgr_import2() will return the existing job. But if we fail to alias + * this job, we will normally want to remove it. But if we did not create + * it in the first place, then we need to avoid removing it. So check + * errno against EEXIST in the success case and if it's EEXIST, then do + * not remove the original job in the event of a failed alias. + * + * This really needs to be re-thought, but I think it'll require a larger + * evaluation of launchd's data structures. Right now, once a job is + * imported into a singleton domain, it won't be removed until the system + * shuts down, but that may not always be true. If it ever changes, we'll + * have a problem because we'll have to account for all existing aliases + * and clean them up somehow. Or just start ref-counting. I knew this + * aliasing stuff would be trouble... + * + * + */ jobmgr_log(where2put, LOG_DEBUG, "Importing service..."); - j = jobmgr_import2(where2put, pload); - if (j) { + + errno = 0; + if ((j = jobmgr_import2(where2put, pload))) { + bool created = (errno != EEXIST); j->xpc_service = true; + if (where2put->xpc_singleton) { /* If the service was destined for one of the global domains, * then we have to alias it into our local domain to reserve the * name. */ - job_t ja = job_new_alias(jm, j); - if (!ja) { + job_t ja = NULL; + if (!(ja = job_new_alias(jm, j))) { /* If we failed to alias the job because of a conflict over * the label, then we remove it from the global domain. We * don't want to risk having imported a malicious job into * one of the global domains. */ if (errno != EEXIST) { - job_assumes(j, errno == 0); + job_log(j, LOG_ERR, "Failed to alias job into: %s: %d: %s", where2put->name, errno, strerror(errno)); } else { - job_log(j, LOG_ERR, "Failed to alias job into: %s", where2put->name); + errno = 0; } - job_remove(j); + if (created) { + jobmgr_log(jm, LOG_WARNING, "Singleton service already existed in job-local namespace. Removing: %s", j->label); + job_remove(j); + } + + j = NULL; } else { + jobmgr_log(jm, LOG_DEBUG, "Aliased service into local domain: %s", j->label); + (void)job_dispatch(j, false); ja->xpc_service = true; j = ja; } + } else { + (void)job_dispatch(j, false); } } + } else { + jobmgr_log(jm, LOG_DEBUG, "Could not find destination for service: %s", label); } return j; } +int +_xpc_domain_import_services(job_t j, launch_data_t services) +{ + int error = EINVAL; + if (launch_data_get_type(services) != LAUNCH_DATA_ARRAY) { + return error; + } + + size_t i = 0; + size_t c = launch_data_array_get_count(services); + jobmgr_log(j->mgr, LOG_DEBUG, "Importing new services: %lu", c); + + for (i = 0; i < c; i++) { + jobmgr_log(j->mgr, LOG_DEBUG, "Importing service at index: %lu", i); + + job_t nj = NULL; + launch_data_t ploadi = launch_data_array_get_index(services, i); + if (!(nj = _xpc_domain_import_service(j->mgr, ploadi))) { + if (!j->mgr->session_initialized && errno) { + /* Service import failures are only fatal if the domain is being + * initialized. If we're extending the domain, we can run into + * errors with services already existing, so we just ignore them. + * In the case of a domain extension, we don't want to halt the + * operation if we run into an error with one service. + * + * + */ + jobmgr_log(j->mgr, LOG_ERR, "Failed to import service at index: %lu: %d: %s", i, errno, strerror(errno)); + error = errno; + break; + } + } else { + jobmgr_log(j->mgr, LOG_DEBUG, "Imported service: %s", nj->label); + } + } + + if (i == c) { + error = 0; + } + + return error; +} + kern_return_t xpc_domain_import2(job_t j, mach_port_t reqport, mach_port_t dport) { @@ -9777,16 +9700,16 @@ xpc_domain_set_environment(job_t j, mach_port_t rp, mach_port_t bsport, mach_por if (!(jm->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) { return BOOTSTRAP_NOT_PRIVILEGED; } - + if (jm->req_asport != MACH_PORT_NULL) { return BOOTSTRAP_NOT_PRIVILEGED; } - + 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) { if (errno != ESRCH) { - jobmgr_assumes(jm, errno == 0); + (void)jobmgr_assumes_zero(jm, errno); } jm->error = errno; @@ -9794,12 +9717,16 @@ xpc_domain_set_environment(job_t j, mach_port_t rp, mach_port_t bsport, mach_por return BOOTSTRAP_NO_MEMORY; } - if (!jobmgr_assumes(jm, audit_session_port(ldc->asid, &jm->req_asport) == 0)) { +#if !TARGET_OS_EMBEDDED + if (jobmgr_assumes_zero(jm, audit_session_port(ldc->asid, &jm->req_asport)) != 0) { jm->error = EPERM; jobmgr_remove(jm); job_log(j, LOG_ERR, "Failed to get port for ASID: %u", ldc->asid); return BOOTSTRAP_NOT_PRIVILEGED; } +#else + 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)); @@ -9812,7 +9739,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; - + return KERN_SUCCESS; } @@ -9823,13 +9750,13 @@ xpc_domain_load_services(job_t j, vm_offset_t services_buff, mach_msg_type_numbe return BOOTSTRAP_UNKNOWN_SERVICE; } - job_t rootj = jobmgr_find_by_pid(root_jobmgr, j->p, false); - if (!(rootj && rootj->xpc_bootstrapper)) { + job_t rootj = jobmgr_find_by_pid(root_jobmgr, j->p, false); + if (!(rootj && rootj->xpc_bootstrapper)) { job_log(j, LOG_ERR, "Attempt to load services into XPC domain by unprivileged job."); return BOOTSTRAP_NOT_PRIVILEGED; } - /* This is just for XPC domains (for now). */ + // This is just for XPC domains (for now). if (!(j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) { return BOOTSTRAP_NOT_PRIVILEGED; } @@ -9840,69 +9767,34 @@ xpc_domain_load_services(job_t j, vm_offset_t services_buff, mach_msg_type_numbe size_t offset = 0; launch_data_t services = launch_data_unpack((void *)services_buff, services_sz, NULL, 0, &offset, NULL); - if (!jobmgr_assumes(j->mgr, services != NULL)) { + if (!services) { return BOOTSTRAP_NO_MEMORY; } - size_t i = 0; - size_t c = launch_data_array_get_count(services); - for (i = 0; i < c; i++) { - job_t nj = NULL; - launch_data_t ploadi = launch_data_array_get_index(services, i); - if (!(nj = xpc_domain_import_service(j->mgr, ploadi))) { - /* If loading one job fails, just fail the whole thing. At this - * point, xpchelper should receive the failure and then just refuse - * to launch the application, since its XPC services could not be - * fully bootstrapped. - * - * Take care to not reference the job or its manager after this - * point. - */ - if (errno == EINVAL) { - jobmgr_log(j->mgr, LOG_ERR, "Service at index is not valid: %lu", i); - } else if (errno == EEXIST) { - /* If we get back EEXIST, we know that the payload was a - * dictionary with a label. But, well, I guess it never hurts to - * check. - */ - char *label = "(bogus)"; - if (launch_data_get_type(ploadi) == LAUNCH_DATA_DICTIONARY) { - launch_data_t llabel = launch_data_dict_lookup(ploadi, LAUNCH_JOBKEY_LABEL); - if (launch_data_get_type(llabel) == LAUNCH_DATA_STRING) { - label = (char *)launch_data_get_string(llabel); - } - } - jobmgr_log(j->mgr, LOG_ERR, "Service name conflict: %s", label); - } - - j->mgr->error = errno; - jobmgr_log(j->mgr, LOG_ERR, "Obliterating domain."); - jobmgr_remove(j->mgr); - break; - } else { - jobmgr_log(j->mgr, LOG_DEBUG, "Imported service %s", nj->label); - job_dispatch(nj, false); - } - } - - kern_return_t result = BOOTSTRAP_NO_MEMORY; - if (i == c) { + int error = _xpc_domain_import_services(j, services); + if (error) { + j->mgr->error = error; + jobmgr_log(j->mgr, LOG_ERR, "Obliterating domain."); + jobmgr_remove(j->mgr); + } else { j->mgr->session_initialized = true; - (void)jobmgr_assumes(j->mgr, xpc_call_wakeup(j->mgr->req_rport, BOOTSTRAP_SUCCESS) == KERN_SUCCESS); + (void)jobmgr_assumes_zero(j->mgr, xpc_call_wakeup(j->mgr->req_rport, BOOTSTRAP_SUCCESS)); j->mgr->req_rport = MACH_PORT_NULL; /* Returning a failure code will destroy the message, whereas returning * success will not, so we need to clean up here. */ mig_deallocate(services_buff, services_sz); - result = BOOTSTRAP_SUCCESS; + error = BOOTSTRAP_SUCCESS; } - return result; + return error; } kern_return_t -xpc_domain_check_in(job_t j, mach_port_t *bsport, mach_port_t *sbsport, mach_port_t *excport, mach_port_t *asport, uint32_t *uid, uint32_t *gid, int32_t *asid, vm_offset_t *ctx, mach_msg_type_number_t *ctx_sz) +xpc_domain_check_in(job_t j, mach_port_t *bsport, mach_port_t *sbsport, + mach_port_t *excport, mach_port_t *asport, uint32_t *uid, uint32_t *gid, + int32_t *asid, vm_offset_t *ctx, mach_msg_type_number_t *ctx_sz) { if (!jobmgr_assumes(root_jobmgr, j != NULL)) { return BOOTSTRAP_UNKNOWN_SERVICE; @@ -9911,11 +9803,11 @@ xpc_domain_check_in(job_t j, mach_port_t *bsport, mach_port_t *sbsport, mach_por if (!(jm->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) { return BOOTSTRAP_NOT_PRIVILEGED; } - + if (jm->req_asport == MACH_PORT_NULL) { return BOOTSTRAP_NOT_PRIVILEGED; } - + *bsport = jm->req_bsport; *sbsport = root_jobmgr->jm_port; *excport = jm->req_excport; @@ -9923,10 +9815,10 @@ xpc_domain_check_in(job_t j, mach_port_t *bsport, mach_port_t *sbsport, mach_por *uid = jm->req_euid; *gid = jm->req_egid; *asid = jm->req_asid; - + *ctx = jm->req_ctx; *ctx_sz = jm->req_ctx_sz; - + return KERN_SUCCESS; } @@ -9943,179 +9835,480 @@ xpc_domain_get_service_name(job_t j, event_name_t name) 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); + jobmgr_log(j->mgr, LOG_ERR, "Attempt to get service name of job with no MachServices: %s", j->label); return BOOTSTRAP_UNKNOWN_SERVICE; } (void)strlcpy(name, ms->name, sizeof(event_name_t)); return BOOTSTRAP_SUCCESS; } -#endif /* __LAUNCH_DISABLE_XPC_SUPPORT__ */ +#if XPC_LPI_VERSION >= 20111216 kern_return_t -xpc_events_get_channel_name(job_t j __attribute__((unused)), event_name_t stream __attribute__((unused)), uint64_t token __attribute__((unused)), event_name_t name __attribute__((unused))) +xpc_domain_add_services(job_t j, vm_offset_t services_buff, mach_msg_type_number_t services_sz) { - return KERN_FAILURE; + if (!j) { + return BOOTSTRAP_UNKNOWN_SERVICE; + } + + job_t rootj = jobmgr_find_by_pid(root_jobmgr, j->p, false); + if (!(rootj && rootj->xpc_bootstrapper)) { + job_log(j, LOG_ERR, "Attempt to add service to XPC domain by unprivileged job."); + return BOOTSTRAP_NOT_PRIVILEGED; + } + + if (!(j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) { + return BOOTSTRAP_NOT_PRIVILEGED; + } + + size_t offset = 0; + launch_data_t services = launch_data_unpack((void *)services_buff, services_sz, NULL, 0, &offset, NULL); + if (!services) { + return BOOTSTRAP_NO_MEMORY; + } + + int error = _xpc_domain_import_services(j, services); + if (!error) { + mig_deallocate(services_buff, services_sz); + } + + return error; } +#endif -kern_return_t -xpc_events_get_event_name(job_t j, event_name_t stream, uint64_t token, event_name_t name) +#pragma mark XPC Events +int +xpc_event_find_channel(job_t j, const char *stream, struct machservice **ms) +{ + int error = EXNOMEM; + struct machservice *msi = NULL; + SLIST_FOREACH(msi, &j->machservices, sle) { + if (strcmp(stream, msi->name) == 0) { + break; + } + } + + if (!msi) { + mach_port_t sp = MACH_PORT_NULL; + msi = machservice_new(j, stream, &sp, false); + if (!msi) { + return EXNOMEM; + } + + job_log(j, LOG_DEBUG, "Creating new MachService for stream: %s", stream); + /* Hack to keep this from being publicly accessible through + * bootstrap_look_up(). + */ + if (!j->dedicated_instance) { + LIST_REMOVE(msi, name_hash_sle); + } + msi->event_channel = true; + + /* If we call job_dispatch() here before the audit session for the job + * has been set, we'll end up not watching this service. But we also have + * to take care not to watch the port if the job is active. + * + * See . + */ + if (!j->currently_ignored) { + machservice_watch(j, msi); + } + + error = 0; + *ms = msi; + } else if (!msi->event_channel) { + job_log(j, LOG_ERR, "This job registered a MachService name identical to the requested event channel name: %s", stream); + error = EEXIST; + } else { + error = 0; + *ms = msi; + } + + return error; +} + +int +xpc_event_get_event_name(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 event name for stream/token: %s/0x%llu", stream, token); + + int result = ESRCH; struct externalevent *event = externalevent_find(stream, token); if (event && j->event_monitor) { - (void)strcpy(name, event->name); - } else { - event = NULL; + xpc_object_t reply2 = xpc_dictionary_create_reply(request); + xpc_dictionary_set_string(reply2, XPC_EVENT_ROUTINE_KEY_NAME, event->name); + *reply = reply2; + + job_log(j, LOG_DEBUG, "Found: %s", event->name); + result = 0; } - return event ? BOOTSTRAP_SUCCESS : BOOTSTRAP_UNKNOWN_SERVICE; + return result; } - -kern_return_t -xpc_events_set_event(job_t j, event_name_t stream, event_name_t key, vm_offset_t event, mach_msg_type_number_t eventCnt) + +int +xpc_event_set_event(job_t j, xpc_object_t request, xpc_object_t *reply) { - if (j->anonymous) { - return BOOTSTRAP_NOT_PRIVILEGED; + const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM); + if (!stream) { + return EXINVAL; + } + + const char *key = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_NAME); + if (!key) { + return EXINVAL; } + xpc_object_t event = xpc_dictionary_get_value(request, XPC_EVENT_ROUTINE_KEY_EVENT); + if (event && xpc_get_type(event) != XPC_TYPE_DICTIONARY) { + return EXINVAL; + } + + job_log(j, LOG_DEBUG, "%s event for stream/key: %s/%s", event ? "Setting" : "Removing", stream, key); + struct externalevent *eei = NULL; LIST_FOREACH(eei, &j->events, job_le) { + /* If the event for the given key already exists for the job, we need to + * remove the old one first. + */ if (strcmp(eei->name, key) == 0 && strcmp(eei->sys->name, stream) == 0) { + job_log(j, LOG_DEBUG, "Event exists. Removing."); externalevent_delete(eei); - eventsystem_ping(); break; } } - bool success = false; - struct eventsystem *es = eventsystem_find(stream); - if (!es) { - es = eventsystem_new(stream); - (void)job_assumes(j, es != NULL); - } + int result = EXNOMEM; + if (event) { + struct eventsystem *es = eventsystem_find(stream); + if (!es) { + job_log(j, LOG_DEBUG, "Creating stream."); + es = eventsystem_new(stream); + } - if (es) { - size_t offset = 0; - launch_data_t unpacked = launch_data_unpack((void *)event, eventCnt, NULL, 0, &offset, 0); - if (unpacked && launch_data_get_type(unpacked) == LAUNCH_DATA_DICTIONARY) { - success = externalevent_new(j, es, key, unpacked); + if (es) { + job_log(j, LOG_DEBUG, "Adding event."); + if (externalevent_new(j, es, key, event)) { + job_log(j, LOG_DEBUG, "Added new event for key: %s", key); + result = 0; + } else { + job_log(j, LOG_ERR, "Could not create event for key: %s", key); + } + } else { + job_log(j, LOG_ERR, "Event stream could not be created: %s", stream); } + } else { + /* If the event was NULL, then we just remove it and return. */ + result = 0; } - if (!success) { - mig_deallocate(event, eventCnt); + if (result == 0) { + xpc_object_t reply2 = xpc_dictionary_create_reply(request); + *reply = reply2; } - return KERN_SUCCESS; + return result; } -kern_return_t -xpc_events_get_event(job_t j, event_name_t stream, event_name_t key, vm_offset_t *event, mach_msg_type_number_t *eventCnt) +int +xpc_event_copy_event(job_t j, xpc_object_t request, xpc_object_t *reply) { + const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM); + const char *key = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_NAME); + + bool all_streams = (stream == NULL); + bool all_events = (key == NULL || strcmp(key, "") == 0); // strcmp for libxpc compatibility + xpc_object_t events = NULL; + + if (all_streams && !all_events) { + return EXINVAL; + } + + if (all_streams || all_events) { + job_log(j, LOG_DEBUG, "Fetching all events%s%s", stream ? " for stream: " : "", stream ? stream : ""); + events = xpc_dictionary_create(NULL, NULL, 0); + } else { + job_log(j, LOG_DEBUG, "Fetching stream/key: %s/%s", stream, key); + } + + int result = ESRCH; struct externalevent *eei = NULL; LIST_FOREACH(eei, &j->events, job_le) { - if (strcmp(eei->name, key) == 0 && strcmp(eei->sys->name, stream) == 0) { - /* Big enough. */ - *eventCnt = 10 * 1024; - mig_allocate(event, *eventCnt); - - size_t sz = launch_data_pack(eei->event, (void *)*event, *eventCnt, NULL, NULL); - if (!job_assumes(j, sz != 0)) { - mig_deallocate(*event, *eventCnt); - return BOOTSTRAP_NO_MEMORY; + if (all_streams) { + xpc_object_t sub = xpc_dictionary_get_value(events, eei->sys->name); + if (sub == NULL) { + sub = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_value(events, eei->sys->name, sub); + xpc_release(sub); + } + xpc_dictionary_set_value(sub, eei->name, eei->event); + } else if (strcmp(eei->sys->name, stream) == 0) { + if (all_events) { + xpc_dictionary_set_value(events, eei->name, eei->event); + } else if (strcmp(eei->name, key) == 0) { + job_log(j, LOG_DEBUG, "Found event."); + events = xpc_retain(eei->event); + break; } - - return BOOTSTRAP_SUCCESS; } } - return BOOTSTRAP_UNKNOWN_SERVICE; + if (events) { + xpc_object_t reply2 = xpc_dictionary_create_reply(request); + xpc_dictionary_set_value(reply2, XPC_EVENT_ROUTINE_KEY_EVENT, events); + xpc_release(events); + + *reply = reply2; + result = 0; + } + + return result; } -struct machservice * -xpc_events_find_channel(job_t j, event_name_t stream, mach_port_t *p) +int +xpc_event_channel_check_in(job_t j, xpc_object_t request, xpc_object_t *reply) { - struct machservice *msi = NULL; - SLIST_FOREACH(msi, &j->machservices, sle) { - if (strcmp(stream, msi->name) == 0) { - break; - } + const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM); + if (!stream) { + return EXINVAL; } - if (!msi) { - mach_port_t sp = MACH_PORT_NULL; - msi = machservice_new(j, stream, &sp, false); - if (job_assumes(j, msi)) { - /* Hack to keep this from being publicly accessible through - * bootstrap_look_up(). - */ - LIST_REMOVE(msi, name_hash_sle); - msi->event_channel = true; - *p = sp; + job_log(j, LOG_DEBUG, "Checking in stream: %s", stream); - /* If we call job_dispatch() here before the audit session for the - * job has been set, we'll end up not watching this service. But we - * also have to take care not to watch the port if the job is - * active. - * - * See . - */ - if (!j->currently_ignored) { - machservice_watch(j, msi); - } - } else { - errno = BOOTSTRAP_NO_MEMORY; - } + struct machservice *ms = NULL; + int error = xpc_event_find_channel(j, stream, &ms); + if (error) { + job_log(j, LOG_ERR, "Failed to check in: 0x%x: %s", error, xpc_strerror(error)); + } else if (ms->isActive) { + job_log(j, LOG_ERR, "Attempt to check in on event channel multiple times: %s", stream); + error = EBUSY; } else { - if (!msi->event_channel) { - job_log(j, LOG_ERR, "This job registered a MachService name identical to the requested event channel name: %s", stream); - msi = NULL; - errno = BOOTSTRAP_NAME_IN_USE; - } else { - *p = msi->port; - } + machservice_request_notifications(ms); + + xpc_object_t reply2 = xpc_dictionary_create_reply(request); + xpc_dictionary_set_mach_recv(reply2, XPC_EVENT_ROUTINE_KEY_PORT, ms->port); + *reply = reply2; + error = 0; } - return msi; + return error; } -kern_return_t -xpc_events_channel_check_in(job_t j, event_name_t stream, uint64_t flags __attribute__((unused)), mach_port_t *p) -{ - struct machservice *ms = xpc_events_find_channel(j, stream, p); - if (ms) { - if (ms->isActive) { - job_log(j, LOG_ERR, "Attempt to check in on event channel multiple times: %s", stream); - *p = MACH_PORT_NULL; - errno = BOOTSTRAP_SERVICE_ACTIVE; - } else { - job_checkin(j); - machservice_request_notifications(ms); - errno = BOOTSTRAP_SUCCESS; +int +xpc_event_channel_look_up(job_t j, xpc_object_t request, xpc_object_t *reply) +{ + if (!j->event_monitor) { + return EPERM; + } + + 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, "Looking up channel for stream/token: %s/%llu", stream, token); + + struct externalevent *ee = externalevent_find(stream, token); + if (!ee) { + return ESRCH; + } + + struct machservice *ms = NULL; + int error = xpc_event_find_channel(ee->job, stream, &ms); + if (!error) { + job_log(j, LOG_DEBUG, "Found event channel port: 0x%x", ms->port); + xpc_object_t reply2 = xpc_dictionary_create_reply(request); + xpc_dictionary_set_mach_send(reply2, XPC_EVENT_ROUTINE_KEY_PORT, ms->port); + *reply = reply2; + error = 0; + } else { + job_log(j, LOG_ERR, "Could not find event channel for stream/token: %s/%llu: 0x%x: %s", stream, token, error, xpc_strerror(error)); + } + + return error; +} + +int +xpc_event_provider_check_in(job_t j, xpc_object_t request, xpc_object_t *reply) +{ + if (!j->event_monitor) { + 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. + */ + j->event_monitor_ready2signal = true; + + const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM); + if (!stream) { + return EXINVAL; + } + + job_log(j, LOG_DEBUG, "Provider checking in for stream: %s", stream); + + xpc_object_t events = xpc_array_create(NULL, 0); + struct eventsystem *es = eventsystem_find(stream); + if (!es) { + /* If we had to create the event stream, there were no events, so just + * give back the empty array. + */ + job_log(j, LOG_DEBUG, "Creating event stream."); + es = eventsystem_new(stream); + if (!job_assumes(j, es)) { + xpc_release(events); + return EXNOMEM; + } + + if (strcmp(stream, "com.apple.launchd.helper") == 0) { + _launchd_support_system = es; + } + } else { + job_log(j, LOG_DEBUG, "Filling event array."); + + struct externalevent *ei = NULL; + LIST_FOREACH(ei, &es->events, sys_le) { + xpc_array_set_uint64(events, XPC_ARRAY_APPEND, ei->id); + xpc_array_append_value(events, ei->event); } } - return errno; + xpc_object_t reply2 = xpc_dictionary_create_reply(request); + xpc_dictionary_set_value(reply2, XPC_EVENT_ROUTINE_KEY_EVENTS, events); + xpc_release(events); + *reply = reply2; + + return 0; } -kern_return_t -xpc_events_channel_look_up(job_t j, event_name_t stream, event_token_t token, uint64_t flags __attribute__((unused)), mach_port_t *p) +int +xpc_event_provider_set_state(job_t j, xpc_object_t request, xpc_object_t *reply) { + job_t other_j = NULL; + if (!j->event_monitor) { - return BOOTSTRAP_NOT_PRIVILEGED; + return EPERM; } - struct externalevent *ee = externalevent_find(stream, token); - if (!ee) { - return BOOTSTRAP_UNKNOWN_SERVICE; + 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; + } + + bool state = false; + xpc_object_t xstate = xpc_dictionary_get_value(request, XPC_EVENT_ROUTINE_KEY_STATE); + if (!xstate || xpc_get_type(xstate) != XPC_TYPE_BOOL) { + return EXINVAL; + } else { + state = xpc_bool_get_value(xstate); + } + + job_log(j, LOG_DEBUG, "Setting event state to %s for stream/token: %s/%llu", state ? "true" : "false", stream, token); + + struct externalevent *ei = externalevent_find(stream, token); + if (!ei) { + job_log(j, LOG_ERR, "Could not find stream/token: %s/%llu", stream, token); + return ESRCH; } - struct machservice *ms = xpc_events_find_channel(ee->job, stream, p); - if (ms) { - errno = BOOTSTRAP_SUCCESS; + other_j = ei->job; + ei->state = state; + + if (ei->internal) { + job_log(ei->job, LOG_NOTICE, "Job should be able to exec(3) now."); + ei->job->waiting4ok = false; + externalevent_delete(ei); } - return errno; + (void)job_dispatch(other_j, false); + + xpc_object_t reply2 = xpc_dictionary_create_reply(request); + *reply = reply2; + + return 0; +} + +bool +xpc_event_demux(mach_port_t p, xpc_object_t request, xpc_object_t *reply) +{ + uint64_t op = xpc_dictionary_get_uint64(request, XPC_EVENT_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); + if (!j || j->anonymous) { + op = -1; + } + + job_log(j, LOG_DEBUG, "Incoming XPC event request: %llu", op); + + int error = -1; + switch (op) { + case XPC_EVENT_GET_NAME: + error = xpc_event_get_event_name(j, request, reply); + break; + case XPC_EVENT_SET: + error = xpc_event_set_event(j, request, reply); + break; + case XPC_EVENT_COPY: + error = xpc_event_copy_event(j, request, reply); + break; + case XPC_EVENT_CHECK_IN: + error = xpc_event_channel_check_in(j, request, reply); + break; + case XPC_EVENT_LOOK_UP: + error = xpc_event_channel_look_up(j, request, reply); + break; + case XPC_EVENT_PROVIDER_CHECK_IN: + error = xpc_event_provider_check_in(j, request, reply); + break; + case XPC_EVENT_PROVIDER_SET_STATE: + error = xpc_event_provider_set_state(j, request, reply); + break; + case -1: + if (j) { + job_log(j, LOG_ERR, "Unmanaged jobs may not make XPC Events requests."); + } + error = EPERM; + break; + default: + job_log(j, LOG_ERR, "Bogus opcode."); + error = EDOM; + } + + if (error) { + xpc_object_t reply2 = xpc_dictionary_create_reply(request); + xpc_dictionary_set_uint64(reply2, XPC_EVENT_ROUTINE_KEY_ERROR, error); + *reply = reply2; + } + + return true; } kern_return_t @@ -10124,7 +10317,7 @@ job_mig_kickstart(job_t j, name_t targetlabel, pid_t *out_pid, unsigned int flag struct ldcred *ldc = runtime_get_caller_creds(); job_t otherj; - if (!launchd_assumes(j != NULL)) { + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -10156,7 +10349,7 @@ job_mig_kickstart(job_t j, name_t targetlabel, pid_t *out_pid, unsigned int flag otherj = job_dispatch(otherj, true); if (!job_assumes(j, otherj && otherj->p)) { - /* Clear this flag if we failed to start the job. */ + // Clear this flag if we failed to start the job. otherj->stall_before_exec = false; return BOOTSTRAP_NO_MEMORY; } @@ -10173,8 +10366,8 @@ job_mig_spawn_internal(job_t j, vm_offset_t indata, mach_msg_type_number_t indat size_t data_offset = 0; struct ldcred *ldc = runtime_get_caller_creds(); job_t jr; - - if (!launchd_assumes(j != NULL)) { + + if (!j) { return BOOTSTRAP_NO_MEMORY; } @@ -10187,7 +10380,7 @@ job_mig_spawn_internal(job_t j, vm_offset_t indata, mach_msg_type_number_t indat return BOOTSTRAP_NOT_PRIVILEGED; } #endif - + if (unlikely(pid1_magic && ldc->euid && ldc->uid)) { job_log(j, LOG_DEBUG, "Punting spawn to per-user-context"); return VPROC_ERR_TRY_PER_USER; @@ -10246,9 +10439,11 @@ job_mig_spawn_internal(job_t j, vm_offset_t indata, mach_msg_type_number_t indat jr->mach_uid = ldc->uid; } + // TODO: Consolidate the app and legacy_LS_job bits. jr->legacy_LS_job = true; jr->abandon_pg = true; jr->asport = asport; + jr->app = true; uuid_clear(jr->expected_audit_uuid); jr = job_dispatch(jr, true); @@ -10291,7 +10486,7 @@ job_mig_spawn2(job_t j, mach_port_t rp, vm_offset_t indata, mach_msg_type_number if (job_dispatch(nj, true)) { if (!was_running) { job_log(nj, LOG_DEBUG, "Job exists but is not running. Kick-starting."); - + if (job_setup_exit_port(nj) == KERN_SUCCESS) { nj->spawn_reply_port = rp; kr = MIG_NO_REPLY; @@ -10313,93 +10508,154 @@ job_mig_spawn2(job_t j, mach_port_t rp, vm_offset_t indata, mach_msg_type_number return kr; } +launch_data_t +job_do_legacy_ipc_request(job_t j, launch_data_t request, mach_port_t asport __attribute__((unused))) +{ + launch_data_t reply = NULL; + + errno = ENOTSUP; + if (launch_data_get_type(request) == LAUNCH_DATA_STRING) { + if (strcmp(launch_data_get_string(request), LAUNCH_KEY_CHECKIN) == 0) { + reply = job_export(j); + job_checkin(j); + } + } + + return reply; +} + +#define LAUNCHD_MAX_LEGACY_FDS 128 +#define countof(x) (sizeof((x)) / sizeof((x[0]))) + kern_return_t -job_mig_event_source_check_in(job_t j, name_t name, mach_port_t ping_port, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt, uint64_t *tokens) +job_mig_legacy_ipc_request(job_t j, vm_offset_t request, + mach_msg_type_number_t requestCnt, mach_port_array_t request_fds, + mach_msg_type_number_t request_fdsCnt, vm_offset_t *reply, + mach_msg_type_number_t *replyCnt, mach_port_array_t *reply_fdps, + mach_msg_type_number_t *reply_fdsCnt, mach_port_t asport) { - if (!j || !j->event_monitor) { - return BOOTSTRAP_NOT_PRIVILEGED; + if (!j) { + return BOOTSTRAP_NO_MEMORY; } - /* Update our ping-port. One ping will force all the notification systems - * to check in, so they'll all give us send-once rights. It doesn't really - * matter which one we keep around. It's not the most efficient thing ever, - * but keep in mind that, by doing this over one channel, we can do it over - * the job's MachService. This means that we'll get it back when the job dies, - * and we can create ourselves a send-once right if we didn't have one already, - * and we can just keep the helper alive without it needing to bootstrap - * communication. - * - * So we're trading efficiency for robustness. In this case, the checkins - * should happen pretty infrequently, so it's pretty worth it. + /* TODO: Once we support actions other than checking in, we must check the + * sandbox capabilities and EUID of the requestort. */ - if (_s_event_update_port != MACH_PORT_NULL) { - (void)job_assumes(j, launchd_mport_deallocate(_s_event_update_port) == KERN_SUCCESS); - } - _s_event_update_port = ping_port; - - kern_return_t result = BOOTSTRAP_NO_MEMORY; - launch_data_t arr = launch_data_alloc(LAUNCH_DATA_ARRAY); - if (job_assumes(j, arr != NULL)) { - struct eventsystem *es = eventsystem_find(name); - if (unlikely(es == NULL)) { - es = eventsystem_new(name); - } - - if (job_assumes(j, es != NULL)) { - struct externalevent *ei = NULL; - size_t i = 0; - LIST_FOREACH(ei, &es->events, sys_le) { - (void)job_assumes(j, launch_data_array_set_index(arr, ei->event, i)); - if (job_assumes(j, i < 1024)) { - tokens[i] = ei->id; - } else { - break; - } - i++; - } - - /* Big enough. */ - *outvalCnt = 10 * 1024; - mig_allocate(outval, *outvalCnt); - - size_t sz = launch_data_pack(arr, (void *)*outval, *outvalCnt, NULL, NULL); - if (job_assumes(j, sz != 0)) { - result = BOOTSTRAP_SUCCESS; - } else { - mig_deallocate(*outval, *outvalCnt); - } + size_t nout_fdps = 0; + size_t nfds = request_fdsCnt / sizeof(request_fds[0]); + if (nfds > LAUNCHD_MAX_LEGACY_FDS) { + job_log(j, LOG_ERR, "Too many incoming descriptors: %lu", nfds); + return BOOTSTRAP_NO_MEMORY; + } + + int in_fds[LAUNCHD_MAX_LEGACY_FDS]; + size_t i = 0; + for (i = 0; i < nfds; i++) { + in_fds[i] = fileport_makefd(request_fds[i]); + if (in_fds[i] == -1) { + job_log(j, LOG_ERR, "Bad descriptor passed in legacy IPC request at index: %lu", i); } + } - /* Total hack, but launch_data doesn't do ref-counting. */ - struct _launch_data *hack = (struct _launch_data *)arr; - free(hack->_array); - free(arr); + // DON'T goto outbad before this point. + *reply = 0; + *reply_fdps = NULL; + launch_data_t ldreply = NULL; + + size_t dataoff = 0; + size_t fdoff = 0; + launch_data_t ldrequest = launch_data_unpack((void *)request, requestCnt, in_fds, nfds, &dataoff, &fdoff); + if (!ldrequest) { + job_log(j, LOG_ERR, "Invalid legacy IPC request passed."); + goto out_bad; } - - return result; -} -kern_return_t -job_mig_event_set_state(job_t j, name_t name, uint64_t token, boolean_t state) -{ - if (!j || !j->event_monitor) { - return BOOTSTRAP_NOT_PRIVILEGED; + ldreply = job_do_legacy_ipc_request(j, ldrequest, asport); + if (!ldreply) { + ldreply = launch_data_new_errno(errno); + if (!ldreply) { + goto out_bad; + } } - - struct externalevent *ei = externalevent_find(name, token); - if (job_assumes(j, ei != NULL)) { - ei->state = state; - if(job_dispatch(ei->job, false) == NULL) { - if (errno == EPERM) { - return BOOTSTRAP_NOT_PRIVILEGED; + + *replyCnt = 10 * 1024 * 1024; + mig_allocate(reply, *replyCnt); + if (!*reply) { + goto out_bad; + } + + int out_fds[LAUNCHD_MAX_LEGACY_FDS]; + size_t nout_fds = 0; + size_t sz = launch_data_pack(ldreply, (void *)*reply, *replyCnt, out_fds, &nout_fds); + if (!sz) { + job_log(j, LOG_ERR, "Could not pack legacy IPC reply."); + goto out_bad; + } + + if (nout_fds) { + if (nout_fds > 128) { + job_log(j, LOG_ERR, "Too many outgoing descriptors: %lu", nout_fds); + goto out_bad; + } + + *reply_fdsCnt = nout_fds * sizeof((*reply_fdps)[0]); + mig_allocate((vm_address_t *)reply_fdps, *reply_fdsCnt); + if (!*reply_fdps) { + goto out_bad; + } + + for (i = 0; i < nout_fds; i++) { + mach_port_t fp = MACH_PORT_NULL; + /* Whatever. Worst case is that we insert MACH_PORT_NULL. Not a big + * deal. Note, these get stuffed into an array whose disposition is + * mach_port_move_send_t, so we don't have to worry about them after + * returning. + */ + if (fileport_makeport(out_fds[i], &fp) != 0) { + job_log(j, LOG_ERR, "Could not pack response descriptor at index: %lu: %d: %s", i, errno, strerror(errno)); } - return BOOTSTRAP_NO_MEMORY; + (*reply_fdps)[i] = fp; } + + nout_fdps = nout_fds; } else { - return BOOTSTRAP_NO_MEMORY; + *reply_fdsCnt = 0; } - + + mig_deallocate(request, requestCnt); + launch_data_free(ldreply); + ldreply = NULL; + + // Unused for now. + (void)launchd_mport_deallocate(asport); + return BOOTSTRAP_SUCCESS; + +out_bad: + for (i = 0; i < nfds; i++) { + (void)close(in_fds[i]); + } + + for (i = 0; i < nout_fds; i++) { + (void)launchd_mport_deallocate((*reply_fdps)[i]); + } + + if (*reply) { + mig_deallocate(*reply, *replyCnt); + } + + /* We should never hit this since the last goto out is in the case that + * allocating this fails. + */ + if (*reply_fdps) { + mig_deallocate((vm_address_t)*reply_fdps, *reply_fdsCnt); + } + + if (ldreply) { + launch_data_free(ldreply); + } + + return BOOTSTRAP_NO_MEMORY; } void @@ -10408,14 +10664,12 @@ jobmgr_init(bool sflag) const char *root_session_type = pid1_magic ? VPROCMGR_SESSION_SYSTEM : VPROCMGR_SESSION_BACKGROUND; SLIST_INIT(&s_curious_jobs); LIST_INIT(&s_needing_sessions); - - launchd_assert((root_jobmgr = jobmgr_new(NULL, MACH_PORT_NULL, MACH_PORT_NULL, sflag, root_session_type, false, MACH_PORT_NULL)) != NULL); -#ifndef __LAUNCH_DISABLE_XPC_SUPPORT__ - launchd_assert((_s_xpc_system_domain = jobmgr_new_xpc_singleton_domain(root_jobmgr, "com.apple.xpc.system")) != NULL); - _s_xpc_system_domain->req_asid = g_audit_session; - _s_xpc_system_domain->req_asport = g_audit_session_port; + + 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); + _s_xpc_system_domain->req_asid = launchd_audit_session; + _s_xpc_system_domain->req_asport = launchd_audit_port; _s_xpc_system_domain->shortdesc = "system"; -#endif /* __LAUNCH_DISABLE_XPC_SUPPORT__ */ if (pid1_magic) { root_jobmgr->monitor_shutdown = true; } @@ -10423,8 +10677,8 @@ jobmgr_init(bool sflag) uint32_t fflags = NOTE_ATTRIB | NOTE_LINK | NOTE_REVOKE | NOTE_EXTEND | NOTE_WRITE; s_no_hang_fd = open("/dev/autofs_nowait", O_EVTONLY | O_NONBLOCK); if (likely(s_no_hang_fd == -1)) { - if (jobmgr_assumes(root_jobmgr, (s_no_hang_fd = open("/dev", O_EVTONLY | O_NONBLOCK)) != -1)) { - (void)jobmgr_assumes(root_jobmgr, kevent_mod((uintptr_t)s_no_hang_fd, EVFILT_VNODE, EV_ADD, fflags, 0, root_jobmgr) != -1); + if (jobmgr_assumes_zero_p(root_jobmgr, (s_no_hang_fd = open("/dev", O_EVTONLY | O_NONBLOCK))) != -1) { + (void)jobmgr_assumes_zero_p(root_jobmgr, kevent_mod((uintptr_t)s_no_hang_fd, EVFILT_VNODE, EV_ADD, fflags, 0, root_jobmgr)); } } s_no_hang_fd = _fd(s_no_hang_fd); @@ -10440,7 +10694,7 @@ our_strhash(const char *s) */ while ((c = *s++)) { - r = ((r << 5) + r) + c; /* hash*33 + c */ + r = ((r << 5) + r) + c; // hash*33 + c } return r; @@ -10477,7 +10731,7 @@ waiting4removal_new(job_t j, mach_port_t rp) void waiting4removal_delete(job_t j, struct waiting_for_removal *w4r) { - (void)job_assumes(j, job_mig_send_signal_reply(w4r->reply_port, 0) == 0); + (void)job_assumes_zero(j, job_mig_send_signal_reply(w4r->reply_port, 0)); SLIST_REMOVE(&j->removal_watchers, w4r, waiting_for_removal, sle); @@ -10490,62 +10744,62 @@ get_kern_max_proc(void) int mib[] = { CTL_KERN, KERN_MAXPROC }; int max = 100; size_t max_sz = sizeof(max); - - (void)launchd_assumes(sysctl(mib, 2, &max, &max_sz, NULL, 0) != -1); - + + (void)posix_assumes_zero(sysctl(mib, 2, &max, &max_sz, NULL, 0)); + return max; } -/* See rdar://problem/6271234 */ +// See rdar://problem/6271234 void eliminate_double_reboot(void) { if (unlikely(!pid1_magic)) { return; } - + struct stat sb; const char *argv[] = { _PATH_BSHELL, "/etc/rc.deferred_install", NULL }; - char *try_again = "Will try again at next boot."; - int result = ~0; - + int result = -1; + if (unlikely(stat(argv[1], &sb) != -1)) { jobmgr_log(root_jobmgr, LOG_DEBUG | LOG_CONSOLE, "Going to run deferred install script."); - - int wstatus; - pid_t p; - - (void)jobmgr_assumes(root_jobmgr, (errno = posix_spawnp(&p, argv[0], NULL, NULL, (char **)argv, environ)) == 0); - - if (errno) { - jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Couldn't run deferred install script! %s", try_again); + + pid_t p = 0; + result = posix_spawnp(&p, argv[0], NULL, NULL, (char **)argv, environ); + if (result == -1) { + jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Couldn't run deferred install script: %d: %s", result, strerror(result)); goto out; } - - if (!jobmgr_assumes(root_jobmgr, waitpid(p, &wstatus, 0) != -1)) { - jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Couldn't confirm that deferred install script exited successfully! %s", try_again); + + int wstatus = 0; + result = waitpid(p, &wstatus, 0); + if (result == -1) { + jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Failed to reap deferred install script: %d: %s", errno, strerror(errno)); goto out; } - - if (jobmgr_assumes(root_jobmgr, WIFEXITED(wstatus) != 0)) { - if (jobmgr_assumes(root_jobmgr, (result = WEXITSTATUS(wstatus)) == EXIT_SUCCESS)) { + + if (WIFEXITED(wstatus)) { + if ((result = WEXITSTATUS(wstatus)) == 0) { jobmgr_log(root_jobmgr, LOG_DEBUG | LOG_CONSOLE, "Deferred install script completed successfully."); } else { - jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Deferred install script exited with status %d. %s", WEXITSTATUS(wstatus), try_again); + jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Deferred install script failed with status: %d", WEXITSTATUS(wstatus)); } } else { - jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Confirmed that deferred install script exited, but couldn't confirm that it was successful. %s", try_again); + jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Weirdness with install script: %d", wstatus); } } out: if (result == 0) { - /* If the unlink(2) was to fail, it would be most likely fail with EBUSY. All the other - * failure cases for unlink(2) don't apply when we're running under PID 1 and have verified - * that the file exists. Outside of someone deliberately messing with us (like if /etc/rc.deferredinstall - * is actually a looping sym-link or a mount point for a filesystem) and I/O errors, we should be good. + /* If the unlink(2) was to fail, it would be most likely fail with + * EBUSY. All the other failure cases for unlink(2) don't apply when + * we're running under PID 1 and have verified that the file exists. + * Outside of someone deliberately messing with us (like if + * /etc/rc.deferredinstall is actually a looping sym-link or a mount + * point for a filesystem) and I/O errors, we should be good. */ - if (!jobmgr_assumes(root_jobmgr, unlink(argv[1]) != -1)) { - jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Deferred install script couldn't be removed!"); + if (unlink(argv[1]) == -1) { + jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Failed to remove deferred install script: %d: %s", errno, strerror(errno)); } } } @@ -10564,6 +10818,8 @@ jetsam_property_setup(launch_data_t obj, const char *key, job_t j) /* 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. */ + } else if (strcasecmp(key, LAUNCH_KEY_JETSAMACTIVE) == 0) { + // Ignore. } else if (strcasecmp(key, LAUNCH_KEY_JETSAMLABEL) == 0) { /* Ignore. This key is present in SpringBoard's request dictionary, so we don't want to * complain about it. @@ -10571,57 +10827,43 @@ jetsam_property_setup(launch_data_t obj, const char *key, job_t j) } else { job_log(j, LOG_ERR, "Unknown Jetsam key: %s", key); } - + if (unlikely(!j->jetsam_properties)) { j->jetsam_properties = true; - LIST_INSERT_HEAD(&j->mgr->jetsam_jobs, j, jetsam_sle); - j->mgr->jetsam_jobs_cnt++; } - - j->jetsam_seq = s_jetsam_sequence_id++; } +#if TARGET_OS_EMBEDDED int launchd_set_jetsam_priorities(launch_data_t priorities) { - if (!launchd_assumes(launch_data_get_type(priorities) == LAUNCH_DATA_ARRAY)) { - return EINVAL; - } + kern_return_t result = 0; - jobmgr_t jm = NULL; -#if !TARGET_OS_EMBEDDED - /* For testing. */ - jm = jobmgr_find_by_name(root_jobmgr, VPROCMGR_SESSION_AQUA); - if (!launchd_assumes(jm != NULL)) { + if (launch_data_get_type(priorities) != LAUNCH_DATA_ARRAY) { return EINVAL; } -#else - /* Since this is for embedded, we can assume that the root job manager holds the Jetsam jobs. */ - jm = root_jobmgr; - - if (!g_embedded_privileged_action) { + if (!launchd_embedded_handofgod) { return EPERM; } -#endif - + 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 (!launchd_assumes(launch_data_get_type(ldi) == LAUNCH_DATA_DICTIONARY)) { + if (launch_data_get_type(ldi) != LAUNCH_DATA_DICTIONARY) { continue; } - - launch_data_t label = NULL; - if (!launchd_assumes(label = launch_data_dict_lookup(ldi, LAUNCH_KEY_JETSAMLABEL))) { + + launch_data_t ldlabel = NULL; + if (!(ldlabel = launch_data_dict_lookup(ldi, LAUNCH_KEY_JETSAMLABEL))) { continue; } - const char *_label = launch_data_get_string(label); - - ji = job_find(NULL, _label); - if (!launchd_assumes(ji != NULL)) { + const char *label = launch_data_get_string(ldlabel); + + ji = job_find(root_jobmgr, label); + if (!ji) { continue; } @@ -10631,65 +10873,34 @@ launchd_set_jetsam_priorities(launch_data_t priorities) 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); } - } - - i = 0; - job_t *jobs = (job_t *)calloc(jm->jetsam_jobs_cnt, sizeof(job_t)); - if (launchd_assumes(jobs != NULL)) { - LIST_FOREACH(ji, &jm->jetsam_jobs, jetsam_sle) { - if (ji->p) { - jobs[i] = ji; - i++; - } - } - } - - size_t totalpris = i; - - int result = EINVAL; - - /* It is conceivable that there could be no Jetsam jobs running. */ - if (totalpris > 0) { - /* Yay blocks! */ - qsort_b((void *)jobs, totalpris, sizeof(job_t), ^ int (const void *lhs, const void *rhs) { - job_t _lhs = *(job_t *)lhs; - job_t _rhs = *(job_t *)rhs; - /* Sort in descending order. (Priority correlates to the soonishness with which you will be killed.) */ - if (_lhs->jetsam_priority > _rhs->jetsam_priority) { - return -1; - } else if (_lhs->jetsam_priority < _rhs->jetsam_priority) { - return 1; - } - /* Priority is equal, so sort by sequence ID to maintain LRU order */ - if( (int)(_lhs->jetsam_seq - _rhs->jetsam_seq) > 0 ) { - return 1; - } else if( (int)(_lhs->jetsam_seq - _rhs->jetsam_seq) < 0 ) { - return -1; - } - - return 0; - }); - - jetsam_priority_entry_t *jpris = (jetsam_priority_entry_t *)calloc(totalpris, sizeof(jetsam_priority_entry_t)); - if (!launchd_assumes(jpris != NULL)) { - result = ENOMEM; - } else { - for (i = 0; i < totalpris; i++) { - jpris[i].pid = jobs[i]->p; /* Subject to time-of-use vs. time-of-check, obviously. */ - jpris[i].flags |= jobs[i]->jetsam_frontmost ? kJetsamFlagsFrontmost : 0; - jpris[i].hiwat_pages = jobs[i]->jetsam_memlimit; - } - - (void)launchd_assumes((result = sysctlbyname("kern.memorystatus_priority_list", NULL, NULL, &jpris[0], totalpris * sizeof(jetsam_priority_entry_t))) != -1); - result = result != 0 ? errno : 0; - - free(jpris); + + 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); } + + launchd_update_jetsam_list(ji); + + result = result != 0 ? errno : 0; } - - if (jobs) { - free(jobs); - } - + + return result; +} + +int +launchd_update_jetsam_list(job_t j) +{ + 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; + + // 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; } +#endif diff --git a/launchd/src/launchd_core_logic.h b/src/core.h similarity index 84% rename from launchd/src/launchd_core_logic.h rename to src/core.h index 0c81246..0bcda03 100644 --- a/launchd/src/launchd_core_logic.h +++ b/src/core.h @@ -1,5 +1,3 @@ -#ifndef __LAUNCHD_CORE_LOGIC__ -#define __LAUNCHD_CORE_LOGIC__ /* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * @@ -20,7 +18,10 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ -#include "launchd_runtime.h" +#ifndef __LAUNCHD_CORE_LOGIC__ +#define __LAUNCHD_CORE_LOGIC__ + +#include "runtime.h" #include "bootstrap.h" #include "launch.h" @@ -28,11 +29,10 @@ typedef struct job_s *job_t; typedef struct jobmgr_s *jobmgr_t; extern jobmgr_t root_jobmgr; -extern mach_port_t inherited_bootstrap_port; -extern mach_port_t g_audit_session_port; -extern au_asid_t g_audit_session; -extern bool g_flat_mach_namespace; -extern bool g_embedded_privileged_action; +extern mach_port_t launchd_audit_port; +extern au_asid_t launchd_audit_session; +extern bool launchd_flat_mach_namespace; +extern bool launchd_embedded_handofgod; void jobmgr_init(bool); jobmgr_t jobmgr_shutdown(jobmgr_t jm); @@ -60,6 +60,11 @@ 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); + +#endif /* __LAUNCHD_CORE_LOGIC__ */ diff --git a/launchd/src/launchd_helper.defs b/src/helper.defs similarity index 73% rename from launchd/src/launchd_helper.defs rename to src/helper.defs index bf69105..2b9a1af 100644 --- a/launchd/src/launchd_helper.defs +++ b/src/helper.defs @@ -1,6 +1,5 @@ #include #include -#include "launchd_mig_types.defs" import "vproc.h"; import "vproc_priv.h"; import "vproc_internal.h"; @@ -10,11 +9,7 @@ subsystem launchd_helper 4241011; userprefix helper_downcall_; serverprefix helper_recv_; -simpleroutine -ping( - p : mach_port_move_send_once_t; -ServerAuditToken token : audit_token_t -); +skip; /* For coreservicesd to harvest exit status, not actually for UserEventAgent. */ simpleroutine diff --git a/launchd/src/launchd_internal.defs b/src/internal.defs similarity index 96% rename from launchd/src/launchd_internal.defs rename to src/internal.defs index 99a1661..d949047 100644 --- a/launchd/src/launchd_internal.defs +++ b/src/internal.defs @@ -18,7 +18,7 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ -subsystem launchd_internal 137000; +subsystem internal 137000; #include #include diff --git a/launchd/src/launchd_unix_ipc.c b/src/ipc.c similarity index 83% rename from launchd/src/launchd_unix_ipc.c rename to src/ipc.c index 0dcd9f9..d6a1d08 100644 --- a/launchd/src/launchd_unix_ipc.c +++ b/src/ipc.c @@ -18,10 +18,8 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ -static const char *const __rcs_file_version__ = "$Revision: 24557 $"; - #include "config.h" -#include "launchd_unix_ipc.h" +#include "ipc.h" #include #include @@ -47,12 +45,13 @@ static const char *const __rcs_file_version__ = "$Revision: 24557 $"; #include #include #include +#include #include "launch.h" #include "launch_priv.h" #include "launchd.h" -#include "launchd_runtime.h" -#include "launchd_core_logic.h" +#include "runtime.h" +#include "core.h" extern char **environ; @@ -82,9 +81,9 @@ ipc_clean_up(void) } if (-1 == unlink(sockpath)) { - runtime_syslog(LOG_WARNING, "unlink(\"%s\"): %s", sockpath, strerror(errno)); + launchd_syslog(LOG_WARNING, "unlink(\"%s\"): %s", sockpath, strerror(errno)); } else if (-1 == rmdir(sockdir)) { - runtime_syslog(LOG_WARNING, "rmdir(\"%s\"): %s", sockdir, strerror(errno)); + launchd_syslog(LOG_WARNING, "rmdir(\"%s\"): %s", sockdir, strerror(errno)); } } @@ -116,18 +115,18 @@ ipc_server_init(void) stat(ourdir, &sb); if (!S_ISDIR(sb.st_mode)) { errno = EEXIST; - runtime_syslog(LOG_ERR, "mkdir(\"%s\"): %s", LAUNCHD_SOCK_PREFIX, strerror(errno)); + launchd_syslog(LOG_ERR, "mkdir(\"%s\"): %s", LAUNCHD_SOCK_PREFIX, strerror(errno)); goto out_bad; } } else { - runtime_syslog(LOG_ERR, "mkdir(\"%s\"): %s", ourdir, strerror(errno)); + launchd_syslog(LOG_ERR, "mkdir(\"%s\"): %s", ourdir, strerror(errno)); goto out_bad; } } } else { snprintf(ourdir, sizeof(ourdir), _PATH_TMP "launchd-%u.XXXXXX", getpid()); if (mkdtemp(ourdir) == NULL) { - runtime_syslog(LOG_ERR, "Could not create critical directory \"%s\": %s", ourdir, strerror(errno)); + launchd_syslog(LOG_ERR, "Could not create critical directory \"%s\": %s", ourdir, strerror(errno)); goto out_bad; } snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/sock", ourdir); @@ -135,12 +134,12 @@ ipc_server_init(void) if (unlink(sun.sun_path) == -1 && errno != ENOENT) { if (errno != EROFS) { - runtime_syslog(LOG_ERR, "unlink(\"thesocket\"): %s", strerror(errno)); + launchd_syslog(LOG_ERR, "unlink(\"thesocket\"): %s", strerror(errno)); } goto out_bad; } - if (!launchd_assumes((fd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) != -1)) { + if (posix_assumes_zero(fd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) { goto out_bad; } @@ -150,18 +149,18 @@ ipc_server_init(void) if (r == -1) { if (errno != EROFS) { - runtime_syslog(LOG_ERR, "bind(\"thesocket\"): %s", strerror(errno)); + launchd_syslog(LOG_ERR, "bind(\"thesocket\"): %s", strerror(errno)); } goto out_bad; } if (listen(fd, SOMAXCONN) == -1) { - runtime_syslog(LOG_ERR, "listen(\"thesocket\"): %s", strerror(errno)); + launchd_syslog(LOG_ERR, "listen(\"thesocket\"): %s", strerror(errno)); goto out_bad; } if (kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &kqipc_listen_callback) == -1) { - runtime_syslog(LOG_ERR, "kevent_mod(\"thesocket\", EVFILT_READ): %s", strerror(errno)); + launchd_syslog(LOG_ERR, "kevent_mod(\"thesocket\", EVFILT_READ): %s", strerror(errno)); goto out_bad; } @@ -174,7 +173,7 @@ ipc_server_init(void) out_bad: if (!ipc_inited && fd != -1) { - (void)launchd_assumes(runtime_close(fd) == 0); + (void)runtime_close(fd); } } @@ -191,7 +190,7 @@ ipc_open(int fd, job_t j) } else { c->conn = launchd_fdopen(fd, -1); } - + c->j = j; LIST_INSERT_HEAD(&connections, c, sle); kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &c->kqconn_callback); @@ -216,11 +215,11 @@ ipc_callback(void *obj, struct kevent *kev) { struct conncb *c = obj; int r; - + if (kev->filter == EVFILT_READ) { if (launchd_msg_recv(c->conn, ipc_readmsg, c) == -1 && errno != EAGAIN) { if (errno != ECONNRESET) { - runtime_syslog(LOG_DEBUG, "%s(): recv: %s", __func__, strerror(errno)); + launchd_syslog(LOG_DEBUG, "%s(): recv: %s", __func__, strerror(errno)); } ipc_close(c); } @@ -228,14 +227,14 @@ ipc_callback(void *obj, struct kevent *kev) r = launchd_msg_send(c->conn, NULL); if (r == -1) { if (errno != EAGAIN) { - runtime_syslog(LOG_DEBUG, "%s(): send: %s", __func__, strerror(errno)); + launchd_syslog(LOG_DEBUG, "%s(): send: %s", __func__, strerror(errno)); ipc_close(c); } } else if (r == 0) { kevent_mod(launchd_getfd(c->conn), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); } } else { - runtime_syslog(LOG_DEBUG, "%s(): unknown filter type!", __func__); + launchd_syslog(LOG_DEBUG, "%s(): unknown filter type!", __func__); ipc_close(c); } } @@ -247,7 +246,7 @@ set_user_env(launch_data_t obj, const char *key, void *context __attribute__((un if (v) { setenv(key, v, 1); } else { - runtime_syslog(LOG_WARNING, "Attempt to set NULL environment variable: %s (type = %d)", key, launch_data_get_type(obj)); + launchd_syslog(LOG_WARNING, "Attempt to set NULL environment variable: %s (type = %d)", key, launch_data_get_type(obj)); } } @@ -278,7 +277,7 @@ ipc_close_fds(launch_data_t o) break; case LAUNCH_DATA_FD: if (launch_data_get_fd(o) != -1) { - (void)launchd_assumes(runtime_close(launch_data_get_fd(o)) == 0); + (void)runtime_close(launch_data_get_fd(o)); } break; default: @@ -335,7 +334,7 @@ ipc_readmsg(launch_data_t msg, void *context) if (errno == EAGAIN) { kevent_mod(launchd_getfd(rmc.c->conn), EVFILT_WRITE, EV_ADD, 0, 0, &rmc.c->kqconn_callback); } else { - runtime_syslog(LOG_DEBUG, "launchd_msg_send() == -1: %s", strerror(errno)); + launchd_syslog(LOG_DEBUG, "launchd_msg_send() == -1: %s", strerror(errno)); ipc_close(rmc.c); } } @@ -353,32 +352,27 @@ ipc_readmsg2(launch_data_t data, const char *cmd, void *context) return; } -// job_log(rmc->c->j, LOG_NOTICE, "Socket IPC request: %s.", cmd); - /* Do not allow commands other than check-in to come over the trusted socket - * on the Desktop. On Embedded, allow all commands over the trusted socket if - * the job has the God Mode key set. + * on the Desktop. On Embedded, allow all commands over the trusted socket + * if the job has the God Mode key set. */ #if TARGET_OS_EMBEDDED - bool allow_privileged_ops = ( !rmc->c->j || job_is_god(rmc->c->j) ); + bool allow_privileged_ops = (!rmc->c->j || job_is_god(rmc->c->j)); #else bool allow_privileged_ops = !rmc->c->j; #endif - + if (rmc->c->j && strcmp(cmd, LAUNCH_KEY_CHECKIN) == 0) { resp = job_export(rmc->c->j); job_checkin(rmc->c->j); } else if (allow_privileged_ops) { - #if TARGET_OS_EMBEDDED - g_embedded_privileged_action = rmc->c->j && job_is_god(rmc->c->j); - #endif +#if TARGET_OS_EMBEDDED + launchd_embedded_handofgod = rmc->c->j && job_is_god(rmc->c->j); +#endif if (data == NULL) { if (!strcmp(cmd, LAUNCH_KEY_SHUTDOWN)) { launchd_shutdown(); resp = launch_data_new_errno(0); - } else if (!strcmp(cmd, LAUNCH_KEY_SINGLEUSER)) { - launchd_single_user(); - resp = launch_data_new_errno(0); } else if (!strcmp(cmd, LAUNCH_KEY_GETJOBS)) { resp = job_export_all(); ipc_revoke_fds(resp); @@ -436,12 +430,16 @@ ipc_readmsg2(launch_data_t data, const char *cmd, void *context) 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 - g_embedded_privileged_action = false; - #endif +#if TARGET_OS_EMBEDDED + launchd_embedded_handofgod = false; +#endif } else { resp = launch_data_new_errno(EACCES); } @@ -466,12 +464,15 @@ ipc_close(struct conncb *c) launch_data_t adjust_rlimits(launch_data_t in) { + /* If I never have to deal with this rlimit nonsense again, I'll be a very + * happy man. + */ struct rlimit l[RLIM_NLIMITS]; struct rlimit *ltmp; size_t i,ltmpsz; for (i = 0; i < RLIM_NLIMITS; i++) { - (void)launchd_assumes(getrlimit(i, l + i) != -1); + (void)posix_assumes_zero(getrlimit(i, l + i)); } if (in) { @@ -479,10 +480,10 @@ adjust_rlimits(launch_data_t in) ltmpsz = launch_data_get_opaque_size(in); if (ltmpsz > sizeof(l)) { - runtime_syslog(LOG_WARNING, "Too much rlimit data sent!"); + launchd_syslog(LOG_WARNING, "Too much rlimit data sent!"); ltmpsz = sizeof(l); } - + for (i = 0; i < (ltmpsz / sizeof(struct rlimit)); i++) { if (ltmp[i].rlim_cur == l[i].rlim_cur && ltmp[i].rlim_max == l[i].rlim_max) { continue; @@ -507,19 +508,19 @@ adjust_rlimits(launch_data_t in) } if (gval > 0) { - (void)launchd_assumes(sysctl(gmib, 2, NULL, NULL, &gval, sizeof(gval)) != -1); + (void)posix_assumes_zero(sysctl(gmib, 2, NULL, NULL, &gval, sizeof(gval))); } else { - runtime_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", gstr); + launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", gstr); } if (pval > 0) { - (void)launchd_assumes(sysctl(pmib, 2, NULL, NULL, &pval, sizeof(pval)) != -1); + (void)posix_assumes_zero(sysctl(pmib, 2, NULL, NULL, &pval, sizeof(pval))); } else { - runtime_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", pstr); + launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", pstr); } } - (void)launchd_assumes(setrlimit(i, ltmp + i) != -1); + (void)posix_assumes_zero(setrlimit(i, ltmp + i)); /* the kernel may have clamped the values we gave it */ - (void)launchd_assumes(getrlimit(i, l + i) != -1); + (void)posix_assumes_zero(getrlimit(i, l + i)); } } diff --git a/launchd/src/launchd_unix_ipc.h b/src/ipc.h similarity index 90% rename from launchd/src/launchd_unix_ipc.h rename to src/ipc.h index 2193a33..202a540 100644 --- a/launchd/src/launchd_unix_ipc.h +++ b/src/ipc.h @@ -1,5 +1,3 @@ -#ifndef __LAUNCHD_UNIX_IPC__ -#define __LAUNCHD_UNIX_IPC__ /* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * @@ -20,10 +18,12 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ -#include +#ifndef __LAUNCHD_IPC_H__ +#define __LAUNCHD_IPC_H__ -#include "launchd_runtime.h" -#include "launchd_core_logic.h" +#include +#include "runtime.h" +#include "core.h" #include "launch_priv.h" #include "launch_internal.h" @@ -44,4 +44,4 @@ void ipc_revoke_fds(launch_data_t o); void ipc_close_fds(launch_data_t o); void ipc_server_init(void); -#endif +#endif /* __LAUNCHD_IPC_H__ */ diff --git a/launchd/src/protocol_vproc.defs b/src/job.defs similarity index 88% rename from launchd/src/protocol_vproc.defs rename to src/job.defs index 702f9fd..cf40b95 100644 --- a/launchd/src/protocol_vproc.defs +++ b/src/job.defs @@ -23,18 +23,15 @@ * Copyright, 1990. All rights reserved. */ -subsystem protocol_vproc 400; +subsystem job 400; #include #include -#include "launchd_mig_types.defs" +#include "job_types.defs" import "vproc.h"; import "vproc_priv.h"; import "vproc_internal.h"; -type mach_port_move_send_array_t = array[] of mach_port_move_send_t - ctype: mach_port_array_t; - userprefix vproc_mig_; serverprefix job_mig_; @@ -120,11 +117,7 @@ subset( out subsetport : mach_port_make_send_t ); -routine -setup_shmem( - j : job_t; -out shmemport : mach_port_move_send_t -); +skip; /* Formerly setup_shmem. */ routine take_subset( @@ -210,7 +203,7 @@ out pid : pid_t; flags : natural_t ); -skip; +skip; /* Formerly embedded_wait. */ routine lookup_children( @@ -229,14 +222,8 @@ switch_to_session( out newbsport : mach_port_make_send_t ); -routine -transaction_count_for_pid( - j : job_t; - pid : pid_t; -out count : integer_t; -out condemned : boolean_t -); - +skip; /* Formerly transaction_count_for_pid. */ + routine pid_is_managed( j : job_t; @@ -267,22 +254,9 @@ set_security_session( skip; /* Formerly wait2. */ -routine -event_source_check_in( - j : job_t; - stream : name_t; - pingport : mach_port_make_send_once_t; -out events : pointer_t, dealloc; -out tokens : event_token_array_t -); +skip; /* Formerly event_source_check_in. */ -routine -event_set_state( - j : job_t; - stream : name_t; - token : uint64_t; - state : boolean_t -); +skip; /* Formerly event_set_state. */ routine spawn2( @@ -299,3 +273,13 @@ get_root_bootstrap( j : job_t; out rootbs : mach_port_move_send_t ); + +routine +legacy_ipc_request( + j : job_t; + request : pointer_t; + request_fds : mach_port_move_send_array_t; +out reply : pointer_t, dealloc; +out reply_fds : mach_port_move_send_array_t, dealloc; + asport : mach_port_t +); diff --git a/launchd/src/protocol_job_forward.defs b/src/job_forward.defs similarity index 97% rename from launchd/src/protocol_job_forward.defs rename to src/job_forward.defs index 92ec153..7999618 100644 --- a/launchd/src/protocol_job_forward.defs +++ b/src/job_forward.defs @@ -22,7 +22,7 @@ subsystem job_forward 400; #include #include -#include "launchd_mig_types.defs" +#include "job_types.defs" import "vproc.h"; import "vproc_priv.h"; import "vproc_internal.h"; diff --git a/launchd/src/protocol_job_reply.defs b/src/job_reply.defs similarity index 91% rename from launchd/src/protocol_job_reply.defs rename to src/job_reply.defs index 8d805f5..c23027c 100644 --- a/launchd/src/protocol_job_reply.defs +++ b/src/job_reply.defs @@ -22,20 +22,20 @@ subsystem job_reply 500; #include #include -#include "launchd_mig_types.defs" +#include "job_types.defs" import "bootstrap.h"; import "vproc.h"; import "vproc_internal.h"; skip; /* create_server */ -skip; /* unprivileged */ +skip; /* reboot2 */ -skip; /* check_in */ +skip; /* check_in2 */ -skip; /* register */ +skip; /* register2 */ -skip; /* look_up */ +skip; /* look_up2 */ simpleroutine job_mig_send_signal_reply( @@ -51,9 +51,9 @@ skip; /* info */ skip; /* subset */ -skip; /* create_service */ +skip; /* setup_shmem */ -skip; /* transfer_subset */ +skip; /* take_subset */ skip; /* getsocket */ @@ -91,7 +91,7 @@ skip; /* lookup_children */ skip; /* switch_to_session */ skip; /* transaction_count_for_pid */ - + skip; /* pid_is_managed */ skip; /* port_for_label */ @@ -115,3 +115,5 @@ job_mig_spawn2_reply( ); skip; /* get_root_bootstrap */ + +skip; /* legacy_ipc_request */ diff --git a/launchd/src/launchd_mig_types.defs b/src/job_types.defs similarity index 51% rename from launchd/src/launchd_mig_types.defs rename to src/job_types.defs index 4688011..f6ba31a 100644 --- a/launchd/src/launchd_mig_types.defs +++ b/src/job_types.defs @@ -23,24 +23,27 @@ * Copyright, 1990. All rights reserved. */ -type pid_t = integer_t; -type pid_array_t = ^array [] of pid_t; -type uid_t = natural_t; -type gid_t = natural_t; -type vproc_gsk_t = integer_t; -type logmsg_t = c_string[*:2048]; -type cmd_t = c_string[512]; -type name_t = c_string[128]; -type name_array_t = ^array [] of name_t; -type bootstrap_property_t = natural_t; -type bootstrap_property_array_t = ^array [] of bootstrap_property_t; -type bootstrap_status_t = integer_t; -type bootstrap_status_array_t = ^array [] of bootstrap_status_t; -type uuid_t = array [16] of MACH_MSG_TYPE_BYTE; -type event_token_array_t = array [1024] of uint64_t; +/* This 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 pid_t = integer_t; +type pid_array_t = ^array [] of pid_t; +type uid_t = natural_t; +type gid_t = natural_t; +type vproc_gsk_t = integer_t; +type logmsg_t = c_string[*:2048]; +type cmd_t = c_string[512]; +type name_t = c_string[128]; +type name_array_t = ^array [] of name_t; +type bootstrap_property_t = natural_t; +type bootstrap_property_array_t = ^array [] of bootstrap_property_t; +type bootstrap_status_t = integer_t; +type bootstrap_status_array_t = ^array [] of bootstrap_status_t; +type uuid_t = array [16] of MACH_MSG_TYPE_BYTE; type job_t = mach_port_t - intran : job_t job_mig_intran(mach_port_t) - outtran : mach_port_t job_mig_outtran(job_t) - destructor : job_mig_destructor(job_t) - cusertype : vproc_mig_t; + intran : job_t job_mig_intran(mach_port_t) + outtran : mach_port_t job_mig_outtran(job_t) + destructor : job_mig_destructor(job_t) + cusertype : vproc_mig_t; diff --git a/launchd/src/launchd_runtime_kill.c b/src/kill2.c similarity index 88% rename from launchd/src/launchd_runtime_kill.c rename to src/kill2.c index ebed8f0..989855e 100644 --- a/launchd/src/launchd_runtime_kill.c +++ b/src/kill2.c @@ -22,10 +22,10 @@ #include #include -#include "launchd_runtime_kill.h" +#include "kill2.h" int -runtime_kill(pid_t pid, int sig) +kill2(pid_t pid, int sig) { /* * POSIX defines consistency over correctness, and consequently @@ -38,7 +38,7 @@ runtime_kill(pid_t pid, int sig) } int -runtime_killpg(pid_t pgrp, int sig) +killpg2(pid_t pgrp, int sig) { - return runtime_kill(-pgrp, sig); + return kill2(-pgrp, sig); } diff --git a/launchd/src/launchd_runtime_kill.h b/src/kill2.h similarity index 82% rename from launchd/src/launchd_runtime_kill.h rename to src/kill2.h index 3b34086..45f7ff1 100644 --- a/launchd/src/launchd_runtime_kill.h +++ b/src/kill2.h @@ -17,12 +17,12 @@ * * @APPLE_APACHE_LICENSE_HEADER_END@ */ -#ifndef __LAUNCHD_RUNTIME_KILL_H__ -#define __LAUNCHD_RUNTIME_KILL_H__ +#ifndef __LAUNCHD_KILL2_H__ +#define __LAUNCHD_KILL2_H__ #include -int runtime_kill(pid_t pid, int sig); -int runtime_killpg(pid_t pgrp, int sig); +int kill2(pid_t pid, int sig); +int killpg2(pid_t pgrp, int sig); -#endif +#endif /* __LAUNCHD_KILL2_H__ */ diff --git a/launchd/src/launchd_ktrace.c b/src/ktrace.c similarity index 84% rename from launchd/src/launchd_ktrace.c rename to src/ktrace.c index 6291708..a815098 100644 --- a/launchd/src/launchd_ktrace.c +++ b/src/ktrace.c @@ -1,4 +1,4 @@ -#include "launchd_ktrace.h" +#include "ktrace.h" void runtime_ktrace1(runtime_ktrace_code_t code) @@ -6,7 +6,7 @@ runtime_ktrace1(runtime_ktrace_code_t code) void *ra = __builtin_extract_return_addr(__builtin_return_address(1)); /* This syscall returns EINVAL when the trace isn't enabled. */ - if (do_apple_internal_logging) { + if (launchd_apple_internal) { syscall(180, code, 0, 0, 0, (long)ra); } } @@ -17,7 +17,7 @@ runtime_ktrace0(runtime_ktrace_code_t code) void *ra = __builtin_extract_return_addr(__builtin_return_address(0)); /* This syscall returns EINVAL when the trace isn't enabled. */ - if (do_apple_internal_logging) { + if (launchd_apple_internal) { syscall(180, code, 0, 0, 0, (long)ra); } } @@ -28,7 +28,7 @@ runtime_ktrace(runtime_ktrace_code_t code, long a, long b, long c) void *ra = __builtin_extract_return_addr(__builtin_return_address(0)); /* This syscall returns EINVAL when the trace isn't enabled. */ - if (do_apple_internal_logging) { + if (launchd_apple_internal) { syscall(180, code, a, b, c, (long)ra); } } diff --git a/launchd/src/launchd_ktrace.h b/src/ktrace.h similarity index 93% rename from launchd/src/launchd_ktrace.h rename to src/ktrace.h index 5e21c5a..691084a 100644 --- a/launchd/src/launchd_ktrace.h +++ b/src/ktrace.h @@ -4,11 +4,11 @@ #include #include -extern bool do_apple_internal_logging; +extern bool launchd_apple_internal; #ifndef DBG_LAUNCHD - #define DBG_LAUNCHD 34 -#endif +#define DBG_LAUNCHD 34 +#endif /* DBG_LAUNCHD */ /* Class(8) | SubClass(8) | Code(14) | Qual(2) */ #define RTKT_CODE(c) ((DBG_LAUNCHD << 24) | (((c) & 0x3fffff) << 2)) diff --git a/launchd/src/launchd.c b/src/launchd.c similarity index 59% rename from launchd/src/launchd.c rename to src/launchd.c index 56e5807..e9fd0e6 100644 --- a/launchd/src/launchd.c +++ b/src/launchd.c @@ -18,8 +18,6 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ -static const char *const __rcs_file_version__ = "$Revision: 24863 $"; - #include "config.h" #include "launchd.h" @@ -66,6 +64,7 @@ static const char *const __rcs_file_version__ = "$Revision: 24863 $"; #include #include #include +#include #if HAVE_LIBAUDITD #include @@ -79,9 +78,9 @@ static const char *const __rcs_file_version__ = "$Revision: 24863 $"; #include "launch.h" #include "launch_internal.h" -#include "launchd_runtime.h" -#include "launchd_core_logic.h" -#include "launchd_unix_ipc.h" +#include "runtime.h" +#include "core.h" +#include "ipc.h" #define LAUNCHD_CONF ".launchd.conf" @@ -104,18 +103,17 @@ static bool do_pid1_crash_diagnosis_mode2(const char *msg); static void *update_thread(void *nothing); -static bool re_exec_in_single_user_mode; static void *crash_addr; static pid_t crash_pid; -bool shutdown_in_progress; -bool fake_shutdown_in_progress; +char *_launchd_database_dir; +char *_launchd_log_dir; + +bool launchd_shutting_down; bool network_up; -char g_username[128] = "__Uninitialized__"; -char g_my_label[128] = "__Uninitialized__"; -char g_launchd_database_dir[PATH_MAX]; -FILE *g_console = NULL; -int32_t g_sync_frequency = 30; +uid_t launchd_uid; +FILE *launchd_console = NULL; +int32_t launchd_sync_frequency = 30; int main(int argc, char *const *argv) @@ -123,11 +121,18 @@ main(int argc, char *const *argv) bool sflag = false; int ch; + /* This needs to be cleaned up. Currently, we risk tripping assumes() macros + * before we've properly set things like launchd's log database paths, the + * global launchd label for syslog messages and the like. Luckily, these are + * operations that will probably never fail, like test_of_openfd(), the + * stuff in launchd_runtime_init() and the stuff in + * handle_pid1_crashes_separately(). + */ testfd_or_openfd(STDIN_FILENO, _PATH_DEVNULL, O_RDONLY); testfd_or_openfd(STDOUT_FILENO, _PATH_DEVNULL, O_WRONLY); testfd_or_openfd(STDERR_FILENO, _PATH_DEVNULL, O_WRONLY); - if (g_use_gmalloc) { + if (launchd_use_gmalloc) { if (!getenv("DYLD_INSERT_LIBRARIES")) { setenv("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib", 1); setenv("MALLOC_STRICT_SIZE", "1", 1); @@ -136,7 +141,7 @@ main(int argc, char *const *argv) unsetenv("DYLD_INSERT_LIBRARIES"); unsetenv("MALLOC_STRICT_SIZE"); } - } else if (g_malloc_log_stacks) { + } else if (launchd_malloc_log_stacks) { if (!getenv("MallocStackLogging")) { setenv("MallocStackLogging", "1", 1); execv(argv[0], argv); @@ -162,94 +167,113 @@ main(int argc, char *const *argv) launchd_runtime_init(); + if (NULL == getenv("PATH")) { + setenv("PATH", _PATH_STDPATH, 1); + } + if (pid1_magic) { + pid1_magic_init(); + int cfd = -1; - if (launchd_assumes((cfd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) != -1)) { + if ((cfd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) != -1) { _fd(cfd); - if (!launchd_assumes((g_console = fdopen(cfd, "w")) != NULL)) { - close(cfd); + if (!(launchd_console = fdopen(cfd, "w"))) { + (void)close(cfd); } } - } - if (NULL == getenv("PATH")) { - setenv("PATH", _PATH_STDPATH, 1); - } + char *extra = ""; + if (launchd_osinstaller) { + extra = " in the OS Installer"; + } else if (sflag) { + extra = " in single-user mode"; + } - if (pid1_magic) { - pid1_magic_init(); - } else { - ipc_server_init(); - - runtime_log_push(); - - struct passwd *pwent = getpwuid(getuid()); - if (pwent) { - strlcpy(g_username, pwent->pw_name, sizeof(g_username) - 1); + launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[1] has started up%s. ***", extra); + if (launchd_use_gmalloc) { + launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Using libgmalloc. ***"); } - snprintf(g_my_label, sizeof(g_my_label), "com.apple.launchd.peruser.%u", getuid()); - - auditinfo_addr_t auinfo; - if (launchd_assumes(getaudit_addr(&auinfo, sizeof(auinfo)) != -1)) { - g_audit_session = auinfo.ai_asid; - runtime_syslog(LOG_DEBUG, "Our audit session ID is %i", g_audit_session); + if (launchd_verbose_boot) { + launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Verbose boot, will log to /dev/console. ***"); } - - g_audit_session_port = _audit_session_self(); - snprintf(g_launchd_database_dir, sizeof(g_launchd_database_dir), LAUNCHD_DB_PREFIX "/com.apple.launchd.peruser.%u", getuid()); - runtime_syslog(LOG_DEBUG, "Per-user launchd for UID %u (%s) has begun.", getuid(), g_username); - } - if (pid1_magic) { - runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[1] has started up. ***"); - if (g_use_gmalloc) { - runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Using libgmalloc. ***"); + if (launchd_shutdown_debugging) { + launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown debugging is enabled. ***"); } - if (g_malloc_log_stacks) { - runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Logging stacks of malloc(3) allocations. ***"); + + if (launchd_log_shutdown) { + launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown logging is enabled. ***"); } - if (g_verbose_boot) { - runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Verbose boot, will log to /dev/console. ***"); + if (launchd_log_perf) { + launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Performance logging is enabled. ***"); } - if (g_shutdown_debugging) { - runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown debugging is enabled. ***"); + if (launchd_log_debug) { + launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Debug logging is enabled. ***"); } + handle_pid1_crashes_separately(); + + /* Start the update thread. + * + * + */ + pthread_t t = NULL; + (void)osx_assumes_zero(pthread_create(&t, NULL, update_thread, NULL)); + (void)osx_assumes_zero(pthread_detach(t)); + /* PID 1 doesn't have a flat namespace. */ - g_flat_mach_namespace = false; + launchd_flat_mach_namespace = false; + fflush(launchd_console); } else { - if (g_use_gmalloc) { - runtime_syslog(LOG_NOTICE, "*** Per-user launchd using libgmalloc. ***"); + launchd_uid = getuid(); + launchd_var_available = true; + if (asprintf(&launchd_label, "com.apple.launchd.peruser.%u", launchd_uid) == 0) { + launchd_label = "com.apple.launchd.peruser.unknown"; } - } - monitor_networking_state(); + struct passwd *pwent = getpwuid(launchd_uid); + if (pwent) { + launchd_username = strdup(pwent->pw_name); + } else { + launchd_username = "(unknown)"; + } - if (pid1_magic) { - handle_pid1_crashes_separately(); - } else { - #if !TARGET_OS_EMBEDDED - /* prime shared memory before the 'bootstrap_port' global is set to zero */ - _vproc_transaction_begin(); - _vproc_transaction_end(); - #endif - } + if (asprintf(&_launchd_database_dir, LAUNCHD_DB_PREFIX "/com.apple.launchd.peruser.%u", launchd_uid) == 0) { + _launchd_database_dir = ""; + } - if (pid1_magic) { - /* Start the update thread -- rdar://problem/5039559&6153301 */ - pthread_t t = NULL; - int err = pthread_create(&t, NULL, update_thread, NULL); - (void)launchd_assumes(err == 0); - (void)launchd_assumes(pthread_detach(t) == 0); + if (asprintf(&_launchd_log_dir, LAUNCHD_LOG_PREFIX "/com.apple.launchd.peruser.%u", launchd_uid) == 0) { + _launchd_log_dir = ""; + } + + if (launchd_allow_global_dyld_envvars) { + launchd_syslog(LOG_WARNING, "Per-user launchd will allow DYLD_* environment variables in the global environment."); + } + + ipc_server_init(); + launchd_log_push(); + + auditinfo_addr_t auinfo; + if (posix_assumes_zero(getaudit_addr(&auinfo, sizeof(auinfo))) != -1) { + launchd_audit_session = auinfo.ai_asid; + launchd_syslog(LOG_DEBUG, "Our audit session ID is %i", launchd_audit_session); + } + + launchd_audit_port = _audit_session_self(); + + vproc_transaction_begin(NULL); + vproc_transaction_end(NULL, NULL); + + launchd_syslog(LOG_DEBUG, "Per-user launchd started (UID/username): %u/%s.", launchd_uid, launchd_username); } + monitor_networking_state(); jobmgr_init(sflag); - - launchd_runtime_init2(); + launchd_runtime_init2(); launchd_runtime(); } @@ -262,25 +286,25 @@ handle_pid1_crashes_separately(void) fsa.sa_flags = SA_SIGINFO; sigemptyset(&fsa.sa_mask); - (void)launchd_assumes(sigaction(SIGILL, &fsa, NULL) != -1); - (void)launchd_assumes(sigaction(SIGFPE, &fsa, NULL) != -1); - (void)launchd_assumes(sigaction(SIGBUS, &fsa, NULL) != -1); - (void)launchd_assumes(sigaction(SIGSEGV, &fsa, NULL) != -1); - (void)launchd_assumes(sigaction(SIGABRT, &fsa, NULL) != -1); - (void)launchd_assumes(sigaction(SIGTRAP, &fsa, NULL) != -1); + (void)posix_assumes_zero(sigaction(SIGILL, &fsa, NULL)); + (void)posix_assumes_zero(sigaction(SIGFPE, &fsa, NULL)); + (void)posix_assumes_zero(sigaction(SIGBUS, &fsa, NULL)); + (void)posix_assumes_zero(sigaction(SIGTRAP, &fsa, NULL)); + (void)posix_assumes_zero(sigaction(SIGABRT, &fsa, NULL)); + (void)posix_assumes_zero(sigaction(SIGSEGV, &fsa, NULL)); } -void *update_thread(void *nothing __attribute__((unused))) +void * +update_thread(void *nothing __attribute__((unused))) { - /* use IOPOL_PASSIVE for sync thread */ - (void)launchd_assumes(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, IOPOL_PASSIVE) != -1); - - while( g_sync_frequency ) { + (void)posix_assumes_zero(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, IOPOL_THROTTLE)); + + while (launchd_sync_frequency) { sync(); - sleep(g_sync_frequency); + sleep(launchd_sync_frequency); } - - runtime_syslog(LOG_DEBUG, "Update thread exiting."); + + launchd_syslog(LOG_DEBUG, "Update thread exiting."); return NULL; } @@ -294,13 +318,13 @@ static __attribute__((unused)) typeof(reboot) *__junk_dyld_trick3 = reboot; void do_pid1_crash_diagnosis_mode(const char *msg) { - if (g_wsp) { - kill(g_wsp, SIGKILL); + if (launchd_wsp) { + kill(launchd_wsp, SIGKILL); sleep(3); - g_wsp = 0; + launchd_wsp = 0; } - while (g_shutdown_debugging && !do_pid1_crash_diagnosis_mode2(msg)) { + while (launchd_shutdown_debugging && !do_pid1_crash_diagnosis_mode2(msg)) { sleep(1); } } @@ -310,10 +334,10 @@ basic_fork(void) { int wstatus = 0; pid_t p; - + switch ((p = fork())) { case -1: - runtime_syslog(LOG_ERR | LOG_CONSOLE, "Can't fork PID 1 copy for crash debugging: %m"); + launchd_syslog(LOG_ERR | LOG_CONSOLE, "Can't fork PID 1 copy for crash debugging: %m"); return p; case 0: return p; @@ -326,7 +350,7 @@ basic_fork(void) return 1; } - + return -1; } @@ -334,23 +358,25 @@ bool do_pid1_crash_diagnosis_mode2(const char *msg) { if (basic_fork() == 0) { - /* Neuter our bootstrap port so that the shell doesn't try talking to us while - * we're blocked waiting on it. + /* Neuter our bootstrap port so that the shell doesn't try talking to us + * while we're blocked waiting on it. */ - if (g_console) { - fflush(g_console); + if (launchd_console) { + fflush(launchd_console); } + task_set_bootstrap_port(mach_task_self(), MACH_PORT_NULL); if (basic_fork() != 0) { - if (g_console) { - fflush(g_console); + if (launchd_console) { + fflush(launchd_console); } + return true; } } else { return true; } - + int fd; revoke(_PATH_CONSOLE); if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1) { @@ -359,6 +385,7 @@ do_pid1_crash_diagnosis_mode2(const char *msg) if (login_tty(fd) == -1) { _exit(3); } + setenv("TERM", "vt100", 1); fprintf(stdout, "\n"); fprintf(stdout, "Entering launchd PID 1 debugging mode...\n"); @@ -371,7 +398,7 @@ do_pid1_crash_diagnosis_mode2(const char *msg) fprintf(stdout, "A sample of PID 1 has been written to %s\n", PID1_CRASH_LOGFILE); fprintf(stdout, "\n"); fflush(stdout); - + execl(_PATH_BSHELL, "-sh", NULL); syslog(LOG_ERR, "can't exec %s for PID 1 crash debugging: %m", _PATH_BSHELL); _exit(EXIT_FAILURE); @@ -388,7 +415,8 @@ fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused))) crash_addr = si->si_addr; crash_pid = si->si_pid; - + + setenv("XPC_SERVICES_UNAVAILABLE", "1", 0); unlink(PID1_CRASH_LOGFILE); switch ((sample_p = vfork())) { @@ -424,57 +452,57 @@ fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused))) void pid1_magic_init(void) { - (void)launchd_assumes(setsid() != -1); - (void)launchd_assumes(chdir("/") != -1); - (void)launchd_assumes(setlogin("root") != -1); - - strcpy(g_my_label, "com.apple.launchd"); + launchd_label = "com.apple.launchd"; + launchd_username = "system"; + + _launchd_database_dir = LAUNCHD_DB_PREFIX "/com.apple.launchd"; + _launchd_log_dir = LAUNCHD_LOG_PREFIX "/com.apple.launchd"; + + (void)posix_assumes_zero(setsid()); + (void)posix_assumes_zero(chdir("/")); + (void)posix_assumes_zero(setlogin("root")); #if !TARGET_OS_EMBEDDED auditinfo_addr_t auinfo = { - .ai_termid = { .at_type = AU_IPv4 }, + .ai_termid = { + .at_type = AU_IPv4 + }, .ai_asid = AU_ASSIGN_ASID, .ai_auid = AU_DEFAUDITID, .ai_flags = AU_SESSION_FLAG_IS_INITIAL, }; - - if (!launchd_assumes(setaudit_addr(&auinfo, sizeof(auinfo)) != -1)) { - runtime_syslog(LOG_WARNING | LOG_CONSOLE, "Could not set audit session: %s.", strerror(errno)); + + if (setaudit_addr(&auinfo, sizeof(auinfo)) == -1) { + launchd_syslog(LOG_WARNING | LOG_CONSOLE, "Could not set audit session: %d: %s.", errno, strerror(errno)); _exit(EXIT_FAILURE); } - g_audit_session = auinfo.ai_asid; - runtime_syslog(LOG_DEBUG, "Audit Session ID: %i", g_audit_session); + launchd_audit_session = auinfo.ai_asid; + launchd_syslog(LOG_DEBUG, "Audit Session ID: %i", launchd_audit_session); - g_audit_session_port = _audit_session_self(); + launchd_audit_port = _audit_session_self(); #endif // !TARGET_OS_EMBEDDED - - strcpy(g_launchd_database_dir, LAUNCHD_DB_PREFIX "/com.apple.launchd"); } char * -launchd_data_base_path(int db_type) +launchd_copy_persistent_store(int type, const char *file) { - static char result[PATH_MAX]; - static int last_db_type = -1; - - if (db_type == last_db_type) { - return result; + char *result = NULL; + if (!file) { + file = ""; } - - switch (db_type) { - case LAUNCHD_DB_TYPE_OVERRIDES : - snprintf(result, sizeof(result), "%s/%s", g_launchd_database_dir, "overrides.plist"); - last_db_type = db_type; - break; - case LAUNCHD_DB_TYPE_JOBCACHE : - snprintf(result, sizeof(result), "%s/%s", g_launchd_database_dir, "jobcache.launchdata"); - last_db_type = db_type; - break; - default : - break; + + switch (type) { + case LAUNCHD_PERSISTENT_STORE_DB: + (void)asprintf(&result, "%s/%s", _launchd_database_dir, file); + break; + case LAUNCHD_PERSISTENT_STORE_LOGS: + (void)asprintf(&result, "%s/%s", _launchd_log_dir, file); + break; + default: + break; } - + return result; } @@ -482,7 +510,7 @@ int _fd(int fd) { if (fd >= 0) { - (void)launchd_assumes(fcntl(fd, F_SETFD, 1) != -1); + (void)posix_assumes_zero(fcntl(fd, F_SETFD, 1)); } return fd; } @@ -492,52 +520,29 @@ launchd_shutdown(void) { int64_t now; - if (shutdown_in_progress) { + if (launchd_shutting_down) { return; } runtime_ktrace0(RTKT_LAUNCHD_EXITING); - shutdown_in_progress = true; - - if (pid1_magic || g_log_per_user_shutdown) { - /* - * When this changes to a more sustainable API, update this: - * http://howto.apple.com/db.cgi?Debugging_Apps_Non-Responsive_At_Shutdown - */ - runtime_setlogmask(LOG_UPTO(LOG_DEBUG)); - } - - runtime_log_push(); + launchd_shutting_down = true; + launchd_log_push(); now = runtime_get_wall_time(); char *term_who = pid1_magic ? "System shutdown" : "Per-user launchd termination for "; - runtime_syslog(LOG_INFO, "%s%s began", term_who, pid1_magic ? "" : g_username); + launchd_syslog(LOG_INFO, "%s%s began", term_who, pid1_magic ? "" : launchd_username); - launchd_assert(jobmgr_shutdown(root_jobmgr) != NULL); + osx_assert(jobmgr_shutdown(root_jobmgr) != NULL); #if HAVE_LIBAUDITD if (pid1_magic) { - (void)launchd_assumes(audit_quick_stop() == 0); + (void)osx_assumes_zero(audit_quick_stop()); } #endif } -void -launchd_single_user(void) -{ - runtime_syslog(LOG_NOTICE, "Going to single-user mode"); - - re_exec_in_single_user_mode = true; - - launchd_shutdown(); - - sleep(3); - - runtime_kill(-1, SIGKILL); -} - void launchd_SessionCreate(void) { @@ -548,12 +553,12 @@ launchd_SessionCreate(void) .ai_auid = getuid(), .ai_flags = 0, }; - if (launchd_assumes(setaudit_addr(&auinfo, sizeof(auinfo)) == 0)) { + if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) { char session[16]; snprintf(session, sizeof(session), "%x", auinfo.ai_asid); setenv("SECURITYSESSIONID", session, 1); } else { - runtime_syslog(LOG_WARNING, "Could not set audit session: %s.", strerror(errno)); + launchd_syslog(LOG_WARNING, "Could not set audit session: %d: %s.", errno, strerror(errno)); } #endif // !TARGET_OS_EMBEDDED } @@ -564,13 +569,13 @@ testfd_or_openfd(int fd, const char *path, int flags) int tmpfd; if (-1 != (tmpfd = dup(fd))) { - (void)launchd_assumes(runtime_close(tmpfd) == 0); + (void)posix_assumes_zero(runtime_close(tmpfd)); } else { if (-1 == (tmpfd = open(path, flags | O_NOCTTY, DEFFILEMODE))) { - runtime_syslog(LOG_ERR, "open(\"%s\", ...): %m", path); + launchd_syslog(LOG_ERR, "open(\"%s\", ...): %m", path); } else if (tmpfd != fd) { - (void)launchd_assumes(dup2(tmpfd, fd) != -1); - (void)launchd_assumes(runtime_close(tmpfd) == 0); + (void)posix_assumes_zero(dup2(tmpfd, fd)); + (void)posix_assumes_zero(runtime_close(tmpfd)); } } } @@ -584,11 +589,11 @@ get_network_state(void) /* Workaround 4978696: getifaddrs() reports false ENOMEM */ while ((r = getifaddrs(&ifa)) == -1 && errno == ENOMEM) { - runtime_syslog(LOG_DEBUG, "Worked around bug: 4978696"); - (void)launchd_assumes(sched_yield() != -1); + launchd_syslog(LOG_DEBUG, "Worked around bug: 4978696"); + (void)posix_assumes_zero(sched_yield()); } - if (!launchd_assumes(r != -1)) { + if (posix_assumes_zero(r) == -1) { return network_up; } @@ -619,7 +624,8 @@ monitor_networking_state(void) network_up = get_network_state(); - if (!launchd_assumes(pfs != -1)) { + if (pfs == -1) { + (void)osx_assumes_zero(errno); return; } @@ -627,12 +633,12 @@ monitor_networking_state(void) kev_req.vendor_code = KEV_VENDOR_APPLE; kev_req.kev_class = KEV_NETWORK_CLASS; - if (!launchd_assumes(ioctl(pfs, SIOCSKEVFILT, &kev_req) != -1)) { + if (posix_assumes_zero(ioctl(pfs, SIOCSKEVFILT, &kev_req)) == -1) { runtime_close(pfs); return; } - (void)launchd_assumes(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback) != -1); + (void)posix_assumes_zero(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback)); } void @@ -641,7 +647,7 @@ pfsystem_callback(void *obj __attribute__((unused)), struct kevent *kev) bool new_networking_state; char buf[1024]; - (void)launchd_assumes(read((int)kev->ident, &buf, sizeof(buf)) != -1); + (void)posix_assumes_zero(read((int)kev->ident, &buf, sizeof(buf))); new_networking_state = get_network_state(); @@ -650,32 +656,3 @@ pfsystem_callback(void *obj __attribute__((unused)), struct kevent *kev) jobmgr_dispatch_all_semaphores(root_jobmgr); } } - -void -_log_launchd_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test) -{ - int saved_errno = errno; - char buf[100]; - const char *file = strrchr(path, '/'); - char *rcs_rev_tmp = strchr(rcs_rev, ' '); - - runtime_ktrace1(RTKT_LAUNCHD_BUG); - - if (!file) { - file = path; - } else { - file += 1; - } - - if (!rcs_rev_tmp) { - strlcpy(buf, rcs_rev, sizeof(buf)); - } else { - strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf)); - rcs_rev_tmp = strchr(buf, ' '); - if (rcs_rev_tmp) { - *rcs_rev_tmp = '\0'; - } - } - - runtime_syslog(LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test); -} diff --git a/launchd/src/launchd.h b/src/launchd.h similarity index 63% rename from launchd/src/launchd.h rename to src/launchd.h index 595df11..d16d6a8 100644 --- a/launchd/src/launchd.h +++ b/src/launchd.h @@ -17,6 +17,7 @@ * * @APPLE_APACHE_LICENSE_HEADER_END@ */ + #ifndef __LAUNCHD_H__ #define __LAUNCHD_H__ @@ -24,35 +25,27 @@ #include #include "launch.h" #include "bootstrap.h" -#include "launchd_runtime.h" +#include "runtime.h" struct kevent; struct conncb; -extern bool shutdown_in_progress; -extern bool fake_shutdown_in_progress; +extern bool pid1_magic; +extern bool launchd_shutting_down; +extern bool fake_launchd_shutting_down; extern bool network_up; -extern bool g_simulate_pid1_crash; -extern FILE *g_console; -extern char g_launchd_database_dir[PATH_MAX]; - -bool init_check_pid(pid_t); +extern FILE *launchd_console; +extern uid_t launchd_uid; -launch_data_t launchd_setstdio(int d, launch_data_t o); void launchd_SessionCreate(void); void launchd_shutdown(void); -void launchd_single_user(void); -boolean_t launchd_mach_ipc_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply); enum { - LAUNCHD_DB_TYPE_OVERRIDES, - LAUNCHD_DB_TYPE_JOBCACHE, - LAUNCHD_DB_TYPE_LAST, + LAUNCHD_PERSISTENT_STORE_DB, + LAUNCHD_PERSISTENT_STORE_LOGS, }; -char *launchd_data_base_path(int db_type); - -void mach_start_shutdown(void); +char *launchd_copy_persistent_store(int type, const char *file); int _fd(int fd); -#endif +#endif /* __LAUNCHD_H__ */ diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..d74e418 --- /dev/null +++ b/src/log.c @@ -0,0 +1,398 @@ +#include +#include +#include "job_reply.h" + +#include "launchd.h" +#include "launch_internal.h" +#include "vproc_internal.h" +#include "log.h" + +#define ROUND_TO_64BIT_WORD_SIZE(x) ((x + 7) & ~7) +#define LAUNCHD_DEBUG_LOG "launchd-debug.%s.log" +#define LAUNCHD_PERF_LOG "launchd-perf.%s.log" +#define LAUNCHD_SHUTDOWN_LOG "launchd-shutdown.%s.log" +#define LAUNCHD_LOWLEVEL_LOG "launchd-lowlevel.%s.log" + +char *launchd_username = "unknown"; +char *launchd_label = "com.apple.launchd.unknown"; +mach_port_t launchd_drain_reply_port; +bool launchd_var_available = false; +int64_t launchd_system_start; + +static FILE *_launchd_shutdown_log; +static FILE *_launchd_debug_log; +static FILE *_launchd_perf_log; +static STAILQ_HEAD(, logmsg_s) _launchd_logq = STAILQ_HEAD_INITIALIZER(_launchd_logq); +static size_t _launchd_logq_sz; +static size_t _launchd_logq_cnt; +static int _launchd_log_up2 = LOG_UPTO(LOG_NOTICE); + +static int64_t _launchd_shutdown_start; + +struct _launchd_open_log_ctx_s { + const char *path; + FILE **filep; +}; + +static void +_launchd_open_log_once(void *ctx) +{ + struct _launchd_open_log_ctx_s *ctx2 = ctx; + const char *path = ctx2->path; + FILE **filep = ctx2->filep; + + char saved[PATH_MAX]; + snprintf(saved, sizeof(saved), "%s.1", path); + (void)rename(path, saved); + + FILE *file = fopen(path, "w"); + if (file) { + (void)_fd(fileno(file)); + *filep = file; + } else if (launchd_console) { + fprintf(launchd_console, "Could not open %s: %d: %s\n", path, errno, strerror(errno)); + } +} + +static void +_launchd_shutdown_start_once(void *ctx __attribute__((unused))) +{ + _launchd_shutdown_start = runtime_get_wall_time(); +} + +int +runtime_setlogmask(int maskpri) +{ + _launchd_log_up2 = maskpri; + return _launchd_log_up2; +} + +static bool +_logmsg_add(struct launchd_syslog_attr *attr, int err_num, const char *msg) +{ + size_t lm_sz = sizeof(struct logmsg_s) + strlen(msg) + strlen(attr->from_name) + strlen(attr->about_name) + strlen(attr->session_name) + 4; + char *data_off; + struct logmsg_s *lm; + + /* Force the unpacking for the log_drain cause unalignment faults. */ + lm_sz = ROUND_TO_64BIT_WORD_SIZE(lm_sz); + + if (unlikely((lm = calloc(1, lm_sz)) == NULL)) { + return false; + } + + data_off = lm->data; + + lm->when = runtime_get_wall_time(); + lm->from_pid = attr->from_pid; + lm->about_pid = attr->about_pid; + lm->err_num = err_num; + lm->pri = attr->priority; + lm->obj_sz = lm_sz; + lm->msg = data_off; + data_off += sprintf(data_off, "%s", msg) + 1; + lm->from_name = data_off; + data_off += sprintf(data_off, "%s", attr->from_name) + 1; + lm->about_name = data_off; + data_off += sprintf(data_off, "%s", attr->about_name) + 1; + lm->session_name = data_off; + data_off += sprintf(data_off, "%s", attr->session_name) + 1; + + STAILQ_INSERT_TAIL(&_launchd_logq, lm, sqe); + _launchd_logq_sz += lm_sz; + _launchd_logq_cnt++; + + return true; +} + +static void +_logmsg_remove(struct logmsg_s *lm) +{ + STAILQ_REMOVE(&_launchd_logq, lm, logmsg_s, sqe); + _launchd_logq_sz -= lm->obj_sz; + _launchd_logq_cnt--; + + free(lm); +} + +bool +_launchd_osx_redirect(const char *message) +{ + launchd_syslog(LOG_ERR, "%s", message); + return true; +} + +void +launchd_syslog(int pri, const char *message, ...) +{ + struct launchd_syslog_attr attr = { + .from_name = launchd_label, + .about_name = launchd_label, + .session_name = pid1_magic ? "System" : "Background", + .priority = pri, + .from_uid = launchd_uid, + .from_pid = getpid(), + .about_pid = getpid(), + }; + + va_list ap; + + va_start(ap, message); + launchd_vsyslog(&attr, message, ap); + va_end(ap); +} + +void +launchd_vsyslog(struct launchd_syslog_attr *attr, const char *fmt, va_list args) +{ + int saved_errno = errno; + char message[2048]; + + static dispatch_once_t perf_once = 0; + static dispatch_once_t shutdown_once = 0; + static dispatch_once_t shutdown_start_once = 0; + static dispatch_once_t debug_once = 0; + + bool echo2console = (attr->priority & LOG_CONSOLE); + attr->priority &= ~LOG_CONSOLE; + if (attr->priority == LOG_APPLEONLY && launchd_apple_internal) { + attr->priority = LOG_NOTICE; + } + + FILE *log2here = NULL; + + /* launchctl is responsible for mounting /var as read-write. So we need to + * wait until /var/log is available before trying to log anything to our log + * anything to our auxilliary log files. This is kind of a hack because + * we'll lose the first few relevant messages at boot for debug and + * performance logging, but the loss isn't too bad. + */ + if (launchd_var_available) { + /* This file is for logging low-level errors where we can't necessarily be + * assured that we can write to the console or use syslog. + */ + char *store = launchd_copy_persistent_store(LAUNCHD_PERSISTENT_STORE_LOGS, NULL); + char path[PATH_MAX]; + + if (attr->priority == LOG_PERF) { + if (launchd_log_perf) { + (void)snprintf(path, sizeof(path), "%s" LAUNCHD_PERF_LOG, store, launchd_username); + + struct _launchd_open_log_ctx_s ctx2 = { + .path = path, + .filep = &_launchd_perf_log, + }; + dispatch_once_f(&perf_once, &ctx2, _launchd_open_log_once); + log2here = _launchd_perf_log; + } + + // Don't log performance messages to the normal syslog store. + attr->priority = LOG_DEBUG + 1; + } else { + if (launchd_shutting_down && launchd_log_shutdown) { + dispatch_once_f(&shutdown_start_once, NULL, _launchd_shutdown_start_once); + + (void)snprintf(path, sizeof(path), "%s" LAUNCHD_SHUTDOWN_LOG, store, launchd_username); + + struct _launchd_open_log_ctx_s ctx2 = { + .path = path, + .filep = &_launchd_shutdown_log, + }; + dispatch_once_f(&shutdown_once, &ctx2, _launchd_open_log_once); + log2here = _launchd_shutdown_log; + } else if (launchd_log_debug) { + (void)snprintf(path, sizeof(path), "%s" LAUNCHD_DEBUG_LOG, store, launchd_username); + + struct _launchd_open_log_ctx_s ctx2 = { + .path = path, + .filep = &_launchd_debug_log, + }; + dispatch_once_f(&debug_once, &ctx2, _launchd_open_log_once); + log2here = _launchd_debug_log; + } + } + + free(store); + + } + + vsnprintf(message, sizeof(message), fmt, args); + if (echo2console && launchd_console) { + fprintf(launchd_console, "%-32s %-8u %-64s %-8u %s\n", attr->from_name, attr->from_pid, attr->about_name, attr->about_pid, message); + } + + if (log2here) { + int64_t delta = 0; + if (log2here == _launchd_shutdown_log) { + delta = runtime_get_wall_time() - _launchd_shutdown_start; + } else { + delta = runtime_get_wall_time() - launchd_system_start; + } + + fprintf(log2here, "%-8lld %-32s %-8u %-24s %-8u %s\n", delta, attr->from_name, attr->from_pid, attr->about_name, attr->about_pid, message); + } + + if ((LOG_MASK(attr->priority) & _launchd_log_up2)) { + _logmsg_add(attr, saved_errno, message); + } +} + +static kern_return_t +_launchd_log_pack(vm_offset_t *outval, mach_msg_type_number_t *outvalCnt) +{ + struct logmsg_s *lm; + void *offset; + + *outvalCnt = _launchd_logq_sz; + + mig_allocate(outval, *outvalCnt); + + if (unlikely(*outval == 0)) { + return 1; + } + + offset = (void *)*outval; + while ((lm = STAILQ_FIRST(&_launchd_logq))) { + lm->from_name_offset = lm->from_name - (char *)lm; + lm->about_name_offset = lm->about_name - (char *)lm; + lm->msg_offset = lm->msg - (char *)lm; + lm->session_name_offset = lm->session_name - (char *)lm; + + memcpy(offset, lm, lm->obj_sz); + + offset += lm->obj_sz; + + _logmsg_remove(lm); + } + + return 0; +} + +static void +_launchd_log_uncork_pending_drain(void) +{ + mach_msg_type_number_t outvalCnt; + mach_port_t tmp_port; + vm_offset_t outval; + + if (!launchd_drain_reply_port) { + return; + } + + if (_launchd_logq_cnt == 0) { + return; + } + + if (_launchd_log_pack(&outval, &outvalCnt) != 0) { + return; + } + + tmp_port = launchd_drain_reply_port; + launchd_drain_reply_port = MACH_PORT_NULL; + + 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)osx_assumes_zero(launchd_mport_deallocate(tmp_port)); + } + + mig_deallocate(outval, outvalCnt); +} + +void +launchd_log_push(void) +{ + vm_offset_t outval = 0; + mach_msg_type_number_t outvalCnt = 0; + + if (!pid1_magic) { + if (_launchd_perf_log) { + (void)fflush(_launchd_perf_log); + } + if (_launchd_shutdown_log) { + (void)fflush(_launchd_shutdown_log); + } + if (_launchd_debug_log) { + (void)fflush(_launchd_debug_log); + } + + if (_launchd_logq_cnt && _launchd_log_pack(&outval, &outvalCnt) == 0) { + (void)_vprocmgr_log_forward(inherited_bootstrap_port, (void *)outval, outvalCnt); + mig_deallocate(outval, outvalCnt); + } + } else { + _launchd_log_uncork_pending_drain(); + } +} + +kern_return_t +launchd_log_forward(uid_t forward_uid, gid_t forward_gid, vm_offset_t inval, mach_msg_type_number_t invalCnt) +{ + struct logmsg_s *lm, *lm_walk; + mach_msg_type_number_t data_left = invalCnt; + + if (inval == 0) { + return 0; + } + + for (lm_walk = (struct logmsg_s *)inval; (data_left > 0) && (lm_walk->obj_sz <= data_left); lm_walk = ((void *)lm_walk + lm_walk->obj_sz)) { + /* malloc(3) returns non-NULL when you ask for zero bytes. If our object + * is zero bytes, something is wrong. + */ + if (lm_walk->obj_sz == 0) { + launchd_syslog(LOG_WARNING, "Encountered a log message of size 0 with %u bytes left in forwarded data. Ignoring remaining messages.", data_left); + break; + } + + if (!(lm = malloc(lm_walk->obj_sz))) { + launchd_syslog(LOG_WARNING, "Failed to allocate %llu bytes for log message with %u bytes left in forwarded data. Ignoring remaining messages.", lm_walk->obj_sz, data_left); + break; + } + + memcpy(lm, lm_walk, lm_walk->obj_sz); + lm->sender_uid = forward_uid; + lm->sender_gid = forward_gid; + + lm->from_name += (size_t)lm; + lm->about_name += (size_t)lm; + lm->msg += (size_t)lm; + lm->session_name += (size_t)lm; + + STAILQ_INSERT_TAIL(&_launchd_logq, lm, sqe); + _launchd_logq_sz += lm->obj_sz; + _launchd_logq_cnt++; + + data_left -= lm->obj_sz; + } + + mig_deallocate(inval, invalCnt); + + return 0; +} + +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); + + 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)); + + return MIG_NO_REPLY; + } + + return _launchd_log_pack(outval, outvalCnt); +} + +void +launchd_closelog(void) +{ + launchd_log_push(); + + if (_launchd_shutdown_log) { + (void)fflush(_launchd_shutdown_log); + (void)runtime_fsync(fileno(_launchd_shutdown_log)); + } +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..4afc53d --- /dev/null +++ b/src/log.h @@ -0,0 +1,71 @@ +#ifndef __LAUNCHD_LOG_H__ +#define __LAUNCHD_LOG_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char *launchd_username; +extern char *launchd_label; +extern mach_port_t launchd_drain_reply_port; +extern bool launchd_var_available; +extern int64_t launchd_system_start; + +struct launchd_syslog_attr { + const char *from_name; + const char *about_name; + const char *session_name; + int priority; + uid_t from_uid; + pid_t from_pid; + pid_t about_pid; +}; + +/* These constants must not have the high bit set so we can safely mask them + * mask them with LOG_CONSOLE. + */ +#define LOG_PERF 0x5252615d +#define LOG_APPLEONLY 0x4141504c +#define LOG_CONSOLE (1 << 31) + +__attribute__((visibility("default"))) +__attribute__((used)) +extern bool +_launchd_osx_redirect(const char *message); + +int +runtime_setlogmask(int maskpri); + +void +launchd_closelog(void); + +__attribute__((format(printf, 2, 3))) +void +launchd_syslog(int pri, const char *message, ...); + +__attribute__((format(printf, 2, 0))) +void +launchd_vsyslog(struct launchd_syslog_attr *attr, const char *message, va_list args); + +void +launchd_log_push(void); + +kern_return_t +launchd_log_forward(uid_t forward_uid, gid_t forward_gid, vm_offset_t inval, mach_msg_type_number_t invalCnt); + +kern_return_t +launchd_log_drain(mach_port_t srp, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt); + +#endif /* __LAUNCHD_LOG_H__ */ diff --git a/launchd/src/protocol_jobmgr.defs b/src/protocol_jobmgr.defs similarity index 100% rename from launchd/src/protocol_jobmgr.defs rename to src/protocol_jobmgr.defs diff --git a/launchd/src/launchd_runtime.c b/src/runtime.c similarity index 50% rename from launchd/src/launchd_runtime.c rename to src/runtime.c index 9514078..b65b7d1 100644 --- a/launchd/src/launchd_runtime.c +++ b/src/runtime.c @@ -18,10 +18,8 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ -static const char *const __rcs_file_version__ = "$Revision: 25122 $"; - #include "config.h" -#include "launchd_runtime.h" +#include "runtime.h" #include #include @@ -60,26 +58,24 @@ static const char *const __rcs_file_version__ = "$Revision: 25122 $"; #include #include #include +#include -#include "launchd_internalServer.h" -#include "launchd_internal.h" +#include "internalServer.h" +#include "internal.h" #include "notifyServer.h" #include "mach_excServer.h" /* We shouldn't be including these */ #include "launch.h" #include "launchd.h" -#include "launchd_core_logic.h" +#include "core.h" #include "vproc.h" #include "vproc_priv.h" #include "vproc_internal.h" -#include "protocol_vprocServer.h" -#include "protocol_job_reply.h" +#include "jobServer.h" +#include "job_reply.h" -#if !TARGET_OS_EMBEDDED -#include "domainServer.h" -#endif /* !TARGET_OS_EMBEDDED */ -#include "eventsServer.h" +#include static mach_port_t ipc_port_set; static mach_port_t demand_port_set; @@ -98,8 +94,7 @@ static kq_callback kqmportset_callback = (kq_callback)mportset_callback; static void *kqueue_demand_loop(void *arg); boolean_t launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply); -static void record_caller_creds(mach_msg_header_t *mh); -static void launchd_runtime2(mach_msg_size_t msg_size, mig_reply_error_t *bufRequest, mig_reply_error_t *bufReply); +static void launchd_runtime2(mach_msg_size_t msg_size); static mach_msg_size_t max_msg_size; static mig_callback *mig_cb_table; static size_t mig_cb_table_sz; @@ -108,16 +103,6 @@ static mach_msg_timeout_t runtime_idle_timeout; static struct ldcred ldc; static size_t runtime_standby_cnt; -static STAILQ_HEAD(, logmsg_s) logmsg_queue = STAILQ_HEAD_INITIALIZER(logmsg_queue); -static size_t logmsg_queue_sz; -static size_t logmsg_queue_cnt; -static mach_port_t drain_reply_port; -static void runtime_log_uncork_pending_drain(void); -static kern_return_t runtime_log_pack(vm_offset_t *outval, mach_msg_type_number_t *outvalCnt); - -static bool logmsg_add(struct runtime_syslog_attr *attr, int err_num, const char *msg); -static void logmsg_remove(struct logmsg_s *lm); - static void do_file_init(void) __attribute__((constructor)); static mach_timebase_info_data_t tbi; static uint64_t tbi_safe_math_max; @@ -129,63 +114,88 @@ static const int sigigns[] = { SIGHUP, SIGINT, SIGPIPE, SIGALRM, SIGTERM, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, SIGINFO, SIGUSR1, SIGUSR2 }; static sigset_t sigign_set; -static FILE *ourlogfile; bool pid1_magic; -bool do_apple_internal_logging; -bool low_level_debug; -bool g_flat_mach_namespace = true; -bool g_simulate_pid1_crash = false; -bool g_malloc_log_stacks = false; -bool g_use_gmalloc = false; -bool g_log_per_user_shutdown = false; +bool launchd_apple_internal; +bool launchd_flat_mach_namespace = true; +bool launchd_malloc_log_stacks = false; +bool launchd_use_gmalloc = false; +bool launchd_log_per_user_shutdown = false; #if !TARGET_OS_EMBEDDED -bool g_log_pid1_shutdown = true; +bool launchd_log_shutdown = true; #else -bool g_log_pid1_shutdown = false; +bool launchd_log_shutdown = false; #endif -bool g_log_strict_usage = false; -bool g_trap_sigkill_bugs = false; -pid_t g_wsp = 0; +bool launchd_log_perf = false; +bool launchd_log_debug = false; +bool launchd_trap_sigkill_bugs = false; +bool launchd_osinstaller = false; +bool launchd_allow_global_dyld_envvars = false; +pid_t launchd_wsp = 0; size_t runtime_busy_cnt; +#if TARGET_OS_EMBEDDED +#define LAUNCHD_CONFIG_PREFIX "/" +#else +#define LAUNCHD_CONFIG_PREFIX "/private/var/db/" +#endif + +#define config_check(s, sb) (stat(LAUNCHD_CONFIG_PREFIX s, &sb) == 0) + mach_port_t runtime_get_kernel_port(void) { return launchd_internal_port; } -// static const char *__crashreporter_info__ = ""; +union vproc_mig_max_sz { + union __RequestUnion__job_mig_job_subsystem req; + union __ReplyUnion__job_mig_job_subsystem rep; +}; -static int internal_mask_pri = LOG_UPTO(LOG_NOTICE); +union internal_max_sz { + union __RequestUnion__x_internal_subsystem req; + union __ReplyUnion__internal_subsystem rep; +}; +union xpc_domain_max_sz { + union __RequestUnion__xpc_domain_xpc_domain_subsystem req; + union __ReplyUnion__xpc_domain_xpc_domain_subsystem rep; +}; + +union mach_exc_max_sz { + union __RequestUnion__catch_mach_exc_subsystem req; + union __ReplyUnion__catch_mach_exc_subsystem rep; +}; + +union do_notify_max_sz { + union __RequestUnion__do_notify_subsystem req; + union __ReplyUnion__do_notify_subsystem rep; +}; void launchd_runtime_init(void) { - mach_msg_size_t mxmsgsz; pid_t p = getpid(); - launchd_assert((mainkq = kqueue()) != -1); + (void)posix_assert_zero((mainkq = kqueue())); - launchd_assert((errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &demand_port_set)) == KERN_SUCCESS); - launchd_assert((errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &ipc_port_set)) == KERN_SUCCESS); + 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)); + posix_assert_zero(kevent_mod(demand_port_set, EVFILT_MACHPORT, EV_ADD, 0, 0, &kqmportset_callback)); - launchd_assert(kevent_mod(demand_port_set, EVFILT_MACHPORT, EV_ADD, 0, 0, &kqmportset_callback) != -1); + osx_assert_zero(launchd_mport_create_recv(&launchd_internal_port)); + osx_assert_zero(launchd_mport_make_send(launchd_internal_port)); - launchd_assert(launchd_mport_create_recv(&launchd_internal_port) == KERN_SUCCESS); - launchd_assert(launchd_mport_make_send(launchd_internal_port) == KERN_SUCCESS); - - /* Sigh... at the moment, MIG has maxsize == sizeof(reply union) */ - mxmsgsz = sizeof(union __RequestUnion__x_launchd_internal_subsystem); - if (x_launchd_internal_subsystem.maxsize > mxmsgsz) { - mxmsgsz = x_launchd_internal_subsystem.maxsize; + 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); } - launchd_assert(runtime_add_mport(launchd_internal_port, launchd_internal_demux, mxmsgsz) == KERN_SUCCESS); - launchd_assert(pthread_create(&kqueue_demand_thread, NULL, kqueue_demand_loop, NULL) == 0); - launchd_assert(pthread_detach(kqueue_demand_thread) == 0); + 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)); - (void)launchd_assumes(sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &p, sizeof(p)) != -1); + (void)posix_assumes_zero(sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &p, sizeof(p))); } void @@ -193,9 +203,10 @@ launchd_runtime_init2(void) { size_t i; + __OSX_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1); for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) { sigaddset(&sigign_set, sigigns[i]); - (void)launchd_assumes(signal(sigigns[i], SIG_IGN) != SIG_ERR); + (void)posix_assumes_zero(signal(sigigns[i], SIG_IGN)); } } @@ -297,7 +308,7 @@ log_kevent_struct(int level, struct kevent *kev_base, int indx) unsigned short flags = kev->flags; unsigned int fflags = kev->fflags; - if (likely(!(LOG_MASK(level) & internal_mask_pri))) { + if (likely(!(LOG_MASK(level & ~LOG_CONSOLE) & LOG_DEBUG))) { return; } @@ -452,7 +463,7 @@ log_kevent_struct(int level, struct kevent *kev_base, int indx) break; } - runtime_syslog(level, "KEVENT[%d]: udata = %p data = 0x%lx ident = %s filter = %s flags = %s fflags = %s", + launchd_syslog(level, "KEVENT[%d]: udata = %p data = 0x%lx ident = %s filter = %s flags = %s fflags = %s", indx, kev->udata, kev->data, ident_buf, filter_str, flags_buf, fflags_buf); } @@ -466,7 +477,7 @@ mportset_callback(void) struct kevent kev; unsigned int i; - if (!launchd_assumes((errno = mach_port_get_set_status(mach_task_self(), demand_port_set, &members, &membersCnt)) == KERN_SUCCESS)) { + if (osx_assumes_zero(mach_port_get_set_status(mach_task_self(), demand_port_set, &members, &membersCnt)) != 0) { return; } @@ -479,7 +490,7 @@ mportset_callback(void) if (status.mps_msgcount) { EV_SET(&kev, members[i], EVFILT_MACHPORT, 0, 0, 0, job_find_by_service_port(members[i])); #if 0 - if (launchd_assumes(kev.udata != NULL)) { + if (kev.udata != NULL) { #endif log_kevent_struct(LOG_DEBUG, &kev, 0); (*((kq_callback *)kev.udata))(kev.udata, &kev); @@ -493,8 +504,7 @@ mportset_callback(void) } } - (void)launchd_assumes(vm_deallocate(mach_task_self(), (vm_address_t)members, - (vm_size_t) membersCnt * sizeof(mach_port_name_t)) == KERN_SUCCESS); + (void)osx_assumes_zero(vm_deallocate(mach_task_self(), (vm_address_t)members, (vm_size_t) membersCnt * sizeof(mach_port_name_t))); } void * @@ -513,8 +523,11 @@ kqueue_demand_loop(void *arg __attribute__((unused))) for (;;) { FD_ZERO(&rfds); FD_SET(mainkq, &rfds); - if (launchd_assumes(select(mainkq + 1, &rfds, NULL, NULL, NULL) == 1)) { - (void)launchd_assumes(handle_kqueue(launchd_internal_port, mainkq) == 0); + int r = select(mainkq + 1, &rfds, NULL, NULL, NULL); + if (r == 1) { + (void)osx_assumes_zero(handle_kqueue(launchd_internal_port, mainkq)); + } else if (posix_assumes_zero(r) != -1) { + (void)osx_assumes_zero(r); } } @@ -530,7 +543,7 @@ x_handle_kqueue(mach_port_t junk __attribute__((unused)), integer_t fd) bulk_kev = kev; - if (launchd_assumes((bulk_kev_cnt = kevent(fd, NULL, 0, kev, BULK_KEV_MAX, &ts)) != -1)) { + if ((bulk_kev_cnt = kevent(fd, NULL, 0, kev, BULK_KEV_MAX, &ts)) != -1) { #if 0 for (i = 0; i < bulk_kev_cnt; i++) { log_kevent_struct(LOG_DEBUG, &kev[0], i); @@ -541,22 +554,9 @@ x_handle_kqueue(mach_port_t junk __attribute__((unused)), integer_t fd) kevi = &kev[i]; if (kevi->filter) { - runtime_syslog(LOG_DEBUG, "Dispatching kevent..."); + launchd_syslog(LOG_DEBUG, "Dispatching kevent (ident/filter): %lu/%hd", kevi->ident, kevi->filter); log_kevent_struct(LOG_DEBUG, kev, i); -#if 0 - /* Check if kevi->udata was either malloc(3)ed or is a valid function pointer. - * If neither, it's probably an invalid pointer and we should log it. - */ - Dl_info dli; - if (launchd_assumes(malloc_size(kevi->udata) || dladdr(kevi->udata, &dli))) { - runtime_ktrace(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_START, kevi->ident, kevi->filter, kevi->fflags); - (*((kq_callback *)kevi->udata))(kevi->udata, kevi); - runtime_ktrace0(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_END); - } else { - runtime_syslog(LOG_ERR, "The following kevent had invalid context data."); - log_kevent_struct(LOG_EMERG, &kev[0], i); - } -#else + struct job_check_s { kq_callback kqc; }; @@ -567,12 +567,14 @@ x_handle_kqueue(mach_port_t junk __attribute__((unused)), integer_t fd) (*((kq_callback *)kevi->udata))(kevi->udata, kevi); runtime_ktrace0(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_END); } else { - runtime_syslog(LOG_ERR, "The following kevent had invalid context data. Please file a bug with the following information:"); + launchd_syslog(LOG_ERR, "The following kevent had invalid context data. Please file a bug with the following information:"); log_kevent_struct(LOG_EMERG, &kev[0], i); } -#endif + launchd_syslog(LOG_DEBUG, "Handled kevent."); } } + } else { + (void)osx_assumes_zero(errno); } bulk_kev = NULL; @@ -583,33 +585,8 @@ x_handle_kqueue(mach_port_t junk __attribute__((unused)), integer_t fd) void launchd_runtime(void) { - mig_reply_error_t *req = NULL, *resp = NULL; - mach_msg_size_t mz = max_msg_size; - int flags = VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE; - - for (;;) { - if (likely(req)) { - (void)launchd_assumes(vm_deallocate(mach_task_self(), (vm_address_t)req, mz) == KERN_SUCCESS); - req = NULL; - } - if (likely(resp)) { - (void)launchd_assumes(vm_deallocate(mach_task_self(), (vm_address_t)resp, mz) == KERN_SUCCESS); - resp = NULL; - } - - mz = max_msg_size; - - if (!launchd_assumes(vm_allocate(mach_task_self(), (vm_address_t *)&req, mz, flags) == KERN_SUCCESS)) { - continue; - } - if (!launchd_assumes(vm_allocate(mach_task_self(), (vm_address_t *)&resp, mz, flags) == KERN_SUCCESS)) { - continue; - } - - launchd_runtime2(mz, req, resp); - - /* If we get here, max_msg_size probably changed... */ - } + launchd_runtime2(max_msg_size); + dispatch_main(); } kern_return_t @@ -631,18 +608,19 @@ launchd_mport_notify_req(mach_port_t name, mach_msg_id_t which) mach_port_t previous, where = (which == MACH_NOTIFY_NO_SENDERS) ? name : launchd_internal_port; if (which == MACH_NOTIFY_NO_SENDERS) { - /* Always make sure the send count is zero, in case a receive right is reused */ + /* Always make sure the send count is zero, in case a receive right is + * reused + */ errno = mach_port_set_mscount(mach_task_self(), name, 0); if (unlikely(errno != KERN_SUCCESS)) { return errno; } } - errno = mach_port_request_notification(mach_task_self(), name, which, msgc, where, - MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); + 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)launchd_assumes(launchd_mport_deallocate(previous) == KERN_SUCCESS); + (void)osx_assumes_zero(launchd_mport_deallocate(previous)); } return errno; @@ -658,13 +636,14 @@ runtime_fork(mach_port_t bsport) sigemptyset(&emptyset); - (void)launchd_assumes(launchd_mport_make_send(bsport) == KERN_SUCCESS); - (void)launchd_assumes(launchd_set_bport(bsport) == KERN_SUCCESS); - (void)launchd_assumes(launchd_mport_deallocate(bsport) == KERN_SUCCESS); + (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)launchd_assumes(sigprocmask(SIG_BLOCK, &sigign_set, &oset) != -1); + __OSX_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)launchd_assumes(signal(sigigns[i], SIG_DFL) != SIG_ERR); + (void)posix_assumes_zero(signal(sigigns[i], SIG_DFL)); } r = fork(); @@ -672,15 +651,14 @@ runtime_fork(mach_port_t bsport) if (r != 0) { for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) { - (void)launchd_assumes(signal(sigigns[i], SIG_IGN) != SIG_ERR); + (void)posix_assumes_zero(signal(sigigns[i], SIG_IGN)); } - (void)launchd_assumes(sigprocmask(SIG_SETMASK, &oset, NULL) != -1); - (void)launchd_assumes(launchd_set_bport(MACH_PORT_NULL) == KERN_SUCCESS); + (void)posix_assumes_zero(sigprocmask(SIG_SETMASK, &oset, NULL)); + (void)osx_assumes_zero(launchd_set_bport(MACH_PORT_NULL)); } else { pid_t p = -getpid(); - (void)launchd_assumes(sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &p, sizeof(p)) != -1); - - (void)launchd_assumes(sigprocmask(SIG_SETMASK, &emptyset, NULL) != -1); + (void)posix_assumes_zero(sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &p, sizeof(p))); + (void)posix_assumes_zero(sigprocmask(SIG_SETMASK, &emptyset, NULL)); } errno = saved_errno; @@ -702,18 +680,16 @@ runtime_set_timeout(timeout_callback to_cb, unsigned int sec) } kern_return_t -runtime_add_mport(mach_port_t name, mig_callback demux, mach_msg_size_t msg_size) +runtime_add_mport(mach_port_t name, mig_callback demux) { size_t needed_table_sz = (MACH_PORT_INDEX(name) + 1) * sizeof(mig_callback); mach_port_t target_set = demux ? ipc_port_set : demand_port_set; - msg_size = round_page(msg_size + MAX_TRAILER_SIZE); - if (unlikely(needed_table_sz > mig_cb_table_sz)) { needed_table_sz *= 2; /* Let's try and avoid realloc'ing for a while */ mig_callback *new_table = malloc(needed_table_sz); - if (!launchd_assumes(new_table != NULL)) { + if (!new_table) { return KERN_RESOURCE_SHORTAGE; } @@ -728,10 +704,6 @@ runtime_add_mport(mach_port_t name, mig_callback demux, mach_msg_size_t msg_size mig_cb_table[MACH_PORT_INDEX(name)] = demux; - if (msg_size > max_msg_size) { - max_msg_size = msg_size; - } - return errno = mach_port_move_member(mach_task_self(), name, target_set); } @@ -805,7 +777,7 @@ kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t case EVFILT_TIMER: /* Workaround 5225889 */ if (flags & EV_ADD) { - kevent_mod(ident, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); + (void)kevent_mod(ident, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); } /* fall through */ default: @@ -815,14 +787,14 @@ kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t flags |= EV_RECEIPT; - if (flags & EV_ADD && !launchd_assumes(udata != NULL)) { + if (flags & EV_ADD && !udata) { errno = EINVAL; return -1; } else if ((flags & EV_DELETE) && bulk_kev) { int i = 0; for (i = bulk_kev_i + 1; i < bulk_kev_cnt; i++) { if (bulk_kev[i].filter == filter && bulk_kev[i].ident == ident) { - runtime_syslog(LOG_DEBUG, "Pruning the following kevent:"); + launchd_syslog(LOG_DEBUG, "Pruning the following kevent:"); log_kevent_struct(LOG_DEBUG, &bulk_kev[0], i); bulk_kev[i].filter = (short)0; } @@ -833,17 +805,19 @@ kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t r = kevent(mainkq, &kev, 1, &kev, 1, NULL); - if (!launchd_assumes(r == 1)) { + if (r != 1) { return -1; } - if (launchd_assumes(kev.flags & EV_ERROR)) { + if (kev.flags & EV_ERROR) { if ((flags & EV_ADD) && kev.data) { - runtime_syslog(LOG_DEBUG, "%s(): See the next line...", __func__); + launchd_syslog(LOG_DEBUG, "%s(): See the next line...", __func__); log_kevent_struct(LOG_DEBUG, &kev, 0); errno = kev.data; return -1; } + } else { + (void)osx_assert_zero(kev.flags); } return r; @@ -852,8 +826,8 @@ kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t boolean_t launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply) { - if (launchd_internal_server_routine(Request)) { - return launchd_internal_server(Request, Reply); + if (internal_server_routine(Request)) { + return internal_server(Request, Reply); } else if (notify_server_routine(Request)) { return notify_server(Request, Reply); } else { @@ -865,9 +839,8 @@ kern_return_t do_mach_notify_port_destroyed(mach_port_t notify __attribute__((unused)), mach_port_t rights) { /* This message is sent to us when a receive right is returned to us. */ - - if (!launchd_assumes(job_ack_port_destruction(rights))) { - (void)launchd_assumes(launchd_mport_close_recv(rights) == KERN_SUCCESS); + if (!job_ack_port_destruction(rights)) { + (void)osx_assumes_zero(launchd_mport_close_recv(rights)); } return KERN_SUCCESS; @@ -889,11 +862,11 @@ do_mach_notify_no_senders(mach_port_t notify, mach_port_mscount_t mscount __attr { job_t j = job_mig_intran(notify); - /* This message is sent to us when the last customer of one of our - * objects goes away. + /* This message is sent to us when the last customer of one of our objects + * goes away. */ - if (!launchd_assumes(j != NULL)) { + if (!j) { return KERN_FAILURE; } @@ -906,8 +879,8 @@ kern_return_t do_mach_notify_send_once(mach_port_t notify __attribute__((unused))) { /* - * This message is sent for each send-once right that is deallocated - * without being used. + * This message is sent for each send-once right that is deallocated without + * being used. */ return KERN_SUCCESS; @@ -916,244 +889,159 @@ do_mach_notify_send_once(mach_port_t notify __attribute__((unused))) kern_return_t do_mach_notify_dead_name(mach_port_t notify __attribute__((unused)), mach_port_name_t name) { - /* This message is sent to us when one of our send rights no longer has - * a receiver somewhere else on the system. + /* This message is sent to us when one of our send rights no longer has a + * receiver somewhere else on the system. */ - - if (name == drain_reply_port) { - (void)launchd_assumes(launchd_mport_deallocate(name) == KERN_SUCCESS); - drain_reply_port = MACH_PORT_NULL; + if (name == launchd_drain_reply_port) { + (void)osx_assumes_zero(launchd_mport_deallocate(name)); + launchd_drain_reply_port = MACH_PORT_NULL; } - if (launchd_assumes(root_jobmgr != NULL)) { + if (root_jobmgr) { root_jobmgr = jobmgr_delete_anything_with_port(root_jobmgr, name); } - /* 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. + /* 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)launchd_assumes(launchd_mport_deallocate(name) == KERN_SUCCESS); + (void)osx_assumes_zero(launchd_mport_deallocate(name)); return KERN_SUCCESS; } -void -record_caller_creds(mach_msg_header_t *mh) -{ - mach_msg_max_trailer_t *tp; - size_t trailer_size; - - tp = (mach_msg_max_trailer_t *)((vm_offset_t)mh + round_msg(mh->msgh_size)); - - trailer_size = tp->msgh_trailer_size - (mach_msg_size_t)(sizeof(mach_msg_trailer_type_t) - sizeof(mach_msg_trailer_size_t)); - - if (launchd_assumes(trailer_size >= (mach_msg_size_t)sizeof(audit_token_t))) { - audit_token_to_au32(tp->msgh_audit, /* audit UID */ NULL, &ldc.euid, - &ldc.egid, &ldc.uid, &ldc.gid, &ldc.pid, - &ldc.asid, /* au_tid_t */ NULL); - } - -} - -struct ldcred * -runtime_get_caller_creds(void) -{ - return &ldc; -} - mach_msg_return_t launchd_exc_runtime_once(mach_port_t port, mach_msg_size_t rcv_msg_size, mach_msg_size_t send_msg_size, mig_reply_error_t *bufRequest, mig_reply_error_t *bufReply, mach_msg_timeout_t to) { mach_msg_return_t mr = ~MACH_MSG_SUCCESS; - mach_msg_option_t rcv_options = MACH_RCV_MSG | - MACH_RCV_TIMEOUT | - MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | - MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) ; - + mach_msg_option_t rcv_options = MACH_RCV_MSG + | MACH_RCV_TIMEOUT + | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) + | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0); + do { mr = mach_msg(&bufRequest->Head, rcv_options, 0, rcv_msg_size, port, to, MACH_PORT_NULL); switch (mr) { - case MACH_RCV_TIMED_OUT : - runtime_syslog(LOG_DEBUG, "Message queue is empty."); - break; - case MACH_RCV_TOO_LARGE : - runtime_syslog(LOG_INFO, "Message is larger than %u bytes.", rcv_msg_size); - break; - default : - (void)launchd_assumes(mr == MACH_MSG_SUCCESS); + case MACH_RCV_TIMED_OUT: + launchd_syslog(LOG_DEBUG, "Message queue is empty."); + break; + case MACH_RCV_TOO_LARGE: + launchd_syslog(LOG_INFO, "Message is larger than %u bytes.", rcv_msg_size); + break; + default: + (void)osx_assumes_zero(mr); } - + if (mr == MACH_MSG_SUCCESS) { - if (!launchd_assumes(mach_exc_server(&bufRequest->Head, &bufReply->Head) == TRUE)) { - runtime_syslog(LOG_WARNING, "Exception server routine failed."); + if (!mach_exc_server(&bufRequest->Head, &bufReply->Head)) { + launchd_syslog(LOG_WARNING, "Exception server routine failed."); break; } - + mach_msg_return_t smr = ~MACH_MSG_SUCCESS; - mach_msg_option_t send_options = MACH_SEND_MSG | - MACH_SEND_TIMEOUT ; - - (void)launchd_assumes(bufReply->Head.msgh_size <= send_msg_size); + mach_msg_option_t send_options = MACH_SEND_MSG | MACH_SEND_TIMEOUT; + + (void)osx_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 : - runtime_syslog(LOG_WARNING, "Timed out while trying to send reply to exception message."); - break; - case MACH_SEND_INVALID_DEST : - runtime_syslog(LOG_WARNING, "Tried sending a message to a port that we don't possess a send right to."); - break; - default : - if (!launchd_assumes(smr == MACH_MSG_SUCCESS)) { - runtime_syslog(LOG_WARNING, "Couldn't deliver exception reply: 0x%x", smr); - } - break; + case MACH_SEND_TIMED_OUT: + launchd_syslog(LOG_WARNING, "Timed out while trying to send reply to exception message."); + break; + case MACH_SEND_INVALID_DEST: + launchd_syslog(LOG_WARNING, "Tried sending a message to a port that we don't possess a send right to."); + break; + default: + if (smr) { + launchd_syslog(LOG_WARNING, "Couldn't deliver exception reply: 0x%x", smr); + } + break; } } } while (0); - + return mr; } void -launchd_runtime2(mach_msg_size_t msg_size, mig_reply_error_t *bufRequest, mig_reply_error_t *bufReply) +runtime_record_caller_creds(audit_token_t *token) { - mach_msg_options_t options, tmp_options; - mig_reply_error_t *bufTemp; - mig_callback the_demux; - mach_msg_timeout_t to; - mach_msg_return_t mr; - size_t busy_cnt; - - options = MACH_RCV_MSG|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | - MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0); + audit_token_to_au32(*token, NULL, &ldc.euid,&ldc.egid, &ldc.uid, &ldc.gid, + &ldc.pid, &ldc.asid, NULL); +} - tmp_options = options; +struct ldcred * +runtime_get_caller_creds(void) +{ + return &ldc; +} - for (;;) { - busy_cnt = runtime_busy_cnt + runtime_standby_cnt; - to = MACH_MSG_TIMEOUT_NONE; - - if (unlikely(msg_size != max_msg_size)) { - /* The buffer isn't big enough to receive messages anymore... */ - tmp_options &= ~MACH_RCV_MSG; - options &= ~MACH_RCV_MSG; - if (!(tmp_options & MACH_SEND_MSG)) { - return; - } - } +static boolean_t +launchd_mig_demux(mach_msg_header_t *request, mach_msg_header_t *reply) +{ + boolean_t result = false; - if ((tmp_options & MACH_RCV_MSG) && (runtime_idle_callback || (busy_cnt == 0))) { - tmp_options |= MACH_RCV_TIMEOUT; + time_of_mach_msg_return = runtime_get_opaque_time(); + launchd_syslog(LOG_DEBUG, "MIG callout: %u", request->msgh_id); + mig_callback the_demux = mig_cb_table[MACH_PORT_INDEX(request->msgh_local_port)]; + mach_msg_audit_trailer_t *tp = (mach_msg_audit_trailer_t *)((vm_offset_t)request + round_msg(request->msgh_size)); + runtime_record_caller_creds(&tp->msgh_audit); - if (!(tmp_options & MACH_SEND_TIMEOUT)) { -#if !TARGET_OS_EMBEDDED - to = busy_cnt ? runtime_idle_timeout : (_vproc_standby_timeout() * 1000); -#else - to = runtime_idle_timeout; -#endif - } + result = the_demux(request, reply); + if (!result) { + launchd_syslog(LOG_DEBUG, "Demux failed. Trying other subsystems..."); + if (request->msgh_id == MACH_NOTIFY_NO_SENDERS) { + launchd_syslog(LOG_DEBUG, "MACH_NOTIFY_NO_SENDERS"); + result = notify_server(request, reply); + } else if (the_demux == job_server) { + launchd_syslog(LOG_DEBUG, "Trying domain subsystem..."); + result = xpc_domain_server(request, reply); + } else { + launchd_syslog(LOG_ERR, "Cannot handle MIG request with ID: 0x%x", request->msgh_id); } + } else { + launchd_syslog(LOG_DEBUG, "MIG demux succeeded."); + } - runtime_log_push(); - - mr = mach_msg(&bufReply->Head, tmp_options, bufReply->Head.msgh_size, - msg_size, ipc_port_set, to, MACH_PORT_NULL); - - time_of_mach_msg_return = runtime_get_opaque_time(); - - tmp_options = options; + return result; +} - /* It looks like the compiler doesn't optimize switch(unlikely(...)) See: 5691066 */ - if (unlikely(mr)) switch (mr) { - case MACH_SEND_INVALID_DEST: - case MACH_SEND_TIMED_OUT: - /* We need to clean up and start over. */ - if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) { - mach_msg_destroy(&bufReply->Head); - } - continue; - case MACH_RCV_TIMED_OUT: - if (to != MACH_MSG_TIMEOUT_NONE) { - if (busy_cnt == 0) { - runtime_syslog(LOG_INFO, "Idle exiting."); - launchd_shutdown(); - } else if (runtime_idle_callback) { - runtime_idle_callback(); - } - } - continue; - default: - if (!launchd_assumes(mr == MACH_MSG_SUCCESS)) { - runtime_syslog(LOG_ERR, "mach_msg(): %u: %s", mr, mach_error_string(mr)); +void +launchd_runtime2(mach_msg_size_t msg_size) +{ + for (;;) { + launchd_log_push(); + + mach_port_t recvp = MACH_PORT_NULL; + 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) { + 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)) { + launchd_syslog(LOG_DEBUG, "XPC routine could not be handled."); + xpc_release(request); + continue; } - continue; - } - bufTemp = bufRequest; - bufRequest = bufReply; - bufReply = bufTemp; - - if (unlikely(!(tmp_options & MACH_RCV_MSG))) { - continue; - } - - /* we have another request message */ -#if 0 - if (!launchd_assumes(mig_cb_table != NULL)) { - break; - } -#endif - - the_demux = mig_cb_table[MACH_PORT_INDEX(bufRequest->Head.msgh_local_port)]; - -#if 0 - if (!launchd_assumes(the_demux != NULL)) { - break; - } -#endif - - record_caller_creds(&bufRequest->Head); - runtime_ktrace(RTKT_LAUNCHD_MACH_IPC|DBG_FUNC_START, bufRequest->Head.msgh_local_port, bufRequest->Head.msgh_id, (long)the_demux); - - if (the_demux(&bufRequest->Head, &bufReply->Head) == FALSE) { - /* XXX - also gross */ - if (likely(bufRequest->Head.msgh_id == MACH_NOTIFY_NO_SENDERS)) { - notify_server(&bufRequest->Head, &bufReply->Head); - } else if (the_demux == protocol_vproc_server) { - -#if !TARGET_OS_EMBEDDED - /* Similarly gross. */ - if (xpc_domain_server(&bufRequest->Head, &bufReply->Head) == FALSE) { - (void)xpc_events_server(&bufRequest->Head, &bufReply->Head); + launchd_syslog(LOG_DEBUG, "XPC routine was handled."); + if (reply) { + launchd_syslog(LOG_DEBUG, "Sending reply."); + result = xpc_pipe_routine_reply(reply); + if (result == 0) { + launchd_syslog(LOG_DEBUG, "Reply sent successfully."); + } else if (result != EPIPE) { + launchd_syslog(LOG_ERR, "Failed to send reply message: 0x%x", result); } -#else - (void)xpc_events_server(&bufRequest->Head, &bufReply->Head); -#endif /* !TARGET_OS_EMBEDDED */ - } - } - runtime_ktrace(RTKT_LAUNCHD_MACH_IPC|DBG_FUNC_END, bufReply->Head.msgh_remote_port, bufReply->Head.msgh_bits, bufReply->RetCode); - - /* bufReply is a union. If MACH_MSGH_BITS_COMPLEX is set, then bufReply->RetCode is assumed to be zero. */ - if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { - if (unlikely(bufReply->RetCode != KERN_SUCCESS)) { - if (likely(bufReply->RetCode == MIG_NO_REPLY)) { - bufReply->Head.msgh_remote_port = MACH_PORT_NULL; - } else if (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) { - /* destroy the request - but not the reply port */ - bufRequest->Head.msgh_remote_port = MACH_PORT_NULL; - mach_msg_destroy(&bufRequest->Head); - } + xpc_release(reply); } - } - if (likely(bufReply->Head.msgh_remote_port != MACH_PORT_NULL)) { - tmp_options |= MACH_SEND_MSG; - - if (unlikely(MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE)) { - tmp_options |= MACH_SEND_TIMEOUT; - } + xpc_release(request); + } else if (result == 0) { + launchd_syslog(LOG_DEBUG, "MIG request."); + } else if (result == EINVAL) { + launchd_syslog(LOG_ERR, "Rejected invalid request message."); } } } @@ -1169,7 +1057,7 @@ runtime_close(int fd) case EVFILT_WRITE: case EVFILT_READ: if (unlikely((int)bulk_kev[i].ident == fd)) { - runtime_syslog(LOG_DEBUG, "Skipping kevent index: %d", i); + launchd_syslog(LOG_DEBUG, "Skipping kevent index: %d", i); bulk_kev[i].filter = 0; } default: @@ -1180,22 +1068,11 @@ runtime_close(int fd) return close(fd); } -void -runtime_closelog(void) -{ - runtime_log_push(); - - if (ourlogfile) { - (void)launchd_assumes(fflush(ourlogfile) == 0); - (void)launchd_assumes(runtime_fsync(fileno(ourlogfile)) != -1); - } -} - int runtime_fsync(int fd) { #if 0 - if (do_apple_internal_logging) { + if (launchd_apple_internal) { return fcntl(fd, F_FULLFSYNC, NULL); } else { return fsync(fd); @@ -1205,334 +1082,6 @@ runtime_fsync(int fd) #endif } -int -runtime_setlogmask(int maskpri) -{ - internal_mask_pri = maskpri; - - return internal_mask_pri; -} - -void -runtime_syslog(int pri, const char *message, ...) -{ - struct runtime_syslog_attr attr = { - g_my_label, - g_my_label, - pid1_magic ? "System" : "Background", - pri, - getuid(), - getpid(), - getpid() - }; - va_list ap; - - va_start(ap, message); - runtime_vsyslog(&attr, message, ap); - - va_end(ap); -} - -void -runtime_vsyslog(struct runtime_syslog_attr *attr, const char *message, va_list args) -{ - int saved_errno = errno; - char newmsg[10000]; - bool echo_to_console= false; - - if (attr->priority == LOG_APPLEONLY) { - if (do_apple_internal_logging) { - attr->priority = LOG_NOTICE; - } else { - return; - } - } else if (attr->priority == LOG_SCOLDING) { - attr->priority = g_log_strict_usage ? LOG_NOTICE : LOG_DEBUG; - } - - if (attr->priority & LOG_CONSOLE) { - echo_to_console = true; - attr->priority &= ~LOG_CONSOLE; - } - - if (!(LOG_MASK(attr->priority) & internal_mask_pri)) { - return; - } - - vsnprintf(newmsg, sizeof(newmsg), message, args); - - if (g_console && (unlikely(low_level_debug) || echo_to_console)) { - fprintf(g_console, "%s %u\t%s %u\t%s\n", attr->from_name, attr->from_pid, attr->about_name, attr->about_pid, newmsg); - } - - logmsg_add(attr, saved_errno, newmsg); -} - -bool -logmsg_add(struct runtime_syslog_attr *attr, int err_num, const char *msg) -{ - size_t lm_sz = sizeof(struct logmsg_s) + strlen(msg) + strlen(attr->from_name) + strlen(attr->about_name) + strlen(attr->session_name) + 4; - char *data_off; - struct logmsg_s *lm; - -#define ROUND_TO_64BIT_WORD_SIZE(x) ((x + 7) & ~7) - - /* we do this to make the unpacking for the log_drain cause unalignment faults */ - lm_sz = ROUND_TO_64BIT_WORD_SIZE(lm_sz); - - if (unlikely((lm = calloc(1, lm_sz)) == NULL)) { - return false; - } - - data_off = lm->data; - - lm->when = runtime_get_wall_time(); - lm->from_pid = attr->from_pid; - lm->about_pid = attr->about_pid; - lm->err_num = err_num; - lm->pri = attr->priority; - lm->obj_sz = lm_sz; - lm->msg = data_off; - data_off += sprintf(data_off, "%s", msg) + 1; - lm->from_name = data_off; - data_off += sprintf(data_off, "%s", attr->from_name) + 1; - lm->about_name = data_off; - data_off += sprintf(data_off, "%s", attr->about_name) + 1; - lm->session_name = data_off; - data_off += sprintf(data_off, "%s", attr->session_name) + 1; - - STAILQ_INSERT_TAIL(&logmsg_queue, lm, sqe); - logmsg_queue_sz += lm_sz; - logmsg_queue_cnt++; - - return true; -} - -void -logmsg_remove(struct logmsg_s *lm) -{ - STAILQ_REMOVE(&logmsg_queue, lm, logmsg_s, sqe); - logmsg_queue_sz -= lm->obj_sz; - logmsg_queue_cnt--; - - free(lm); -} - -kern_return_t -runtime_log_pack(vm_offset_t *outval, mach_msg_type_number_t *outvalCnt) -{ - struct logmsg_s *lm; - void *offset; - - *outvalCnt = logmsg_queue_sz; - - mig_allocate(outval, *outvalCnt); - - if (unlikely(*outval == 0)) { - return 1; - } - - offset = (void *)*outval; - - if (g_log_per_user_shutdown && !ourlogfile && !pid1_magic && shutdown_in_progress) { - char logfile[NAME_MAX]; - snprintf(logfile, sizeof(logfile), "/var/tmp/launchd-%s.shutdown.log", g_username); - - char logfile1[NAME_MAX]; - snprintf(logfile1, sizeof(logfile1), "/var/tmp/launchd-%s.shutdown.log.1", g_username); - - rename(logfile, logfile1); - ourlogfile = fopen(logfile, "a"); - } - - static int64_t shutdown_start = 0; - if (shutdown_start == 0) { - shutdown_start = runtime_get_wall_time(); - } - - while ((lm = STAILQ_FIRST(&logmsg_queue))) { - int64_t log_delta = lm->when - shutdown_start; - if (!pid1_magic && ourlogfile) { - fprintf(ourlogfile, "%8lld%6u %-40s%6u %-40s %s\n", log_delta, - lm->from_pid, lm->from_name, lm->about_pid, lm->about_name, lm->msg); - fflush(ourlogfile); - } - - lm->from_name_offset = lm->from_name - (char *)lm; - lm->about_name_offset = lm->about_name - (char *)lm; - lm->msg_offset = lm->msg - (char *)lm; - lm->session_name_offset = lm->session_name - (char *)lm; - - memcpy(offset, lm, lm->obj_sz); - - offset += lm->obj_sz; - - logmsg_remove(lm); - } - - if (ourlogfile) { - fflush(ourlogfile); - } - - return 0; -} - -void -runtime_log_uncork_pending_drain(void) -{ - mach_msg_type_number_t outvalCnt; - mach_port_t tmp_port; - vm_offset_t outval; - - if (!drain_reply_port) { - return; - } - - if (logmsg_queue_cnt == 0) { - return; - } - - if (runtime_log_pack(&outval, &outvalCnt) != 0) { - return; - } - - tmp_port = drain_reply_port; - drain_reply_port = MACH_PORT_NULL; - - if (unlikely(errno = job_mig_log_drain_reply(tmp_port, 0, outval, outvalCnt))) { - (void)launchd_assumes(errno == MACH_SEND_INVALID_DEST); - (void)launchd_assumes(launchd_mport_deallocate(tmp_port) == KERN_SUCCESS); - } - - mig_deallocate(outval, outvalCnt); -} - -void -runtime_log_push(void) -{ - static pthread_mutex_t ourlock = PTHREAD_MUTEX_INITIALIZER; - static int64_t shutdown_start, log_delta; - mach_msg_type_number_t outvalCnt; - struct logmsg_s *lm; - vm_offset_t outval; - - if (logmsg_queue_cnt == 0) { - (void)launchd_assumes(STAILQ_EMPTY(&logmsg_queue)); - return; - } else if (!pid1_magic) { - if (runtime_log_pack(&outval, &outvalCnt) == 0) { - (void)launchd_assumes(_vprocmgr_log_forward(inherited_bootstrap_port, (void *)outval, outvalCnt) == NULL); - mig_deallocate(outval, outvalCnt); - } - return; - } - - if (likely(!shutdown_in_progress && !fake_shutdown_in_progress)) { - runtime_log_uncork_pending_drain(); - return; - } - - if (unlikely(shutdown_start == 0)) { - shutdown_start = runtime_get_wall_time(); - launchd_log_vm_stats(); - } - - pthread_mutex_lock(&ourlock); - - if (unlikely(ourlogfile == NULL) && g_log_pid1_shutdown) { - rename("/var/log/launchd-shutdown.log", "/var/log/launchd-shutdown.log.1"); - ourlogfile = fopen("/var/log/launchd-shutdown.log", "a"); - } - - pthread_mutex_unlock(&ourlock); - - if (unlikely(!ourlogfile)) { - return; - } - - while ((lm = STAILQ_FIRST(&logmsg_queue))) { - log_delta = lm->when - shutdown_start; - - fprintf(ourlogfile, "%8lld%6u %-40s%6u %-40s %s\n", log_delta, - lm->from_pid, lm->from_name, lm->about_pid, lm->about_name, lm->msg); - - logmsg_remove(lm); - } - - fflush(ourlogfile); -} - -kern_return_t -runtime_log_forward(uid_t forward_uid, gid_t forward_gid, vm_offset_t inval, mach_msg_type_number_t invalCnt) -{ - struct logmsg_s *lm, *lm_walk; - mach_msg_type_number_t data_left = invalCnt; - - if (inval == 0) { - return 0; - } - - for (lm_walk = (struct logmsg_s *)inval; (data_left > 0) && (lm_walk->obj_sz <= data_left); lm_walk = ((void *)lm_walk + lm_walk->obj_sz)) { - /* malloc() does not return NULL if you ask it for an allocation of size 0. - * It will return a valid pointer that can be passed to free(). If we don't - * do this check, we'll wind up corrupting our heap in the subsequent - * assignments. - * - * We break out if this check fails because, obj_sz is supposed to include - * the size of the logmsg_s struct. If it claims to be of zero size, we - * can't safely increment our counter because something obviously got screwed - * up along the way, since this should always be at least sizeof(struct logmsg_s). - */ - if (!launchd_assumes(lm_walk->obj_sz > 0)) { - runtime_syslog(LOG_WARNING, "Encountered a log message of size 0 with %u bytes left in forwarded data. Ignoring remaining messages.", data_left); - break; - } - - /* If malloc() keeps failing, we shouldn't put additional pressure on the system - * by attempting to add more messages to the log until it returns success - * log a failure, hope pressure lets off, and move on. - */ - if (!launchd_assumes(lm = malloc(lm_walk->obj_sz))) { - runtime_syslog(LOG_WARNING, "Failed to allocate %llu bytes for log message with %u bytes left in forwarded data. Ignoring remaining messages.", lm_walk->obj_sz, data_left); - break; - } - - memcpy(lm, lm_walk, lm_walk->obj_sz); - lm->sender_uid = forward_uid; - lm->sender_gid = forward_gid; - - lm->from_name += (size_t)lm; - lm->about_name += (size_t)lm; - lm->msg += (size_t)lm; - lm->session_name += (size_t)lm; - - STAILQ_INSERT_TAIL(&logmsg_queue, lm, sqe); - logmsg_queue_sz += lm->obj_sz; - logmsg_queue_cnt++; - - data_left -= lm->obj_sz; - } - - mig_deallocate(inval, invalCnt); - - return 0; -} - -kern_return_t -runtime_log_drain(mach_port_t srp, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt) -{ - (void)launchd_assumes(drain_reply_port == 0); - - if ((logmsg_queue_cnt == 0) || shutdown_in_progress || fake_shutdown_in_progress) { - drain_reply_port = srp; - (void)launchd_assumes(launchd_mport_notify_req(drain_reply_port, MACH_NOTIFY_DEAD_NAME) == KERN_SUCCESS); - - return MIG_NO_REPLY; - } - - return runtime_log_pack(outval, outvalCnt); -} - /* * We should break this into two reference counts. * @@ -1546,12 +1095,13 @@ void runtime_add_ref(void) { if (!pid1_magic) { - #if !TARGET_OS_EMBEDDED - _vproc_transaction_begin(); - #endif +#if !TARGET_OS_EMBEDDED + vproc_transaction_begin(NULL); +#endif } - + runtime_busy_cnt++; + launchd_syslog(LOG_PERF, "Incremented busy count. Now: %lu", runtime_busy_cnt); runtime_remove_timer(); } @@ -1559,17 +1109,17 @@ void runtime_del_ref(void) { if (!pid1_magic) { - #if !TARGET_OS_EMBEDDED +#if !TARGET_OS_EMBEDDED if (_vproc_transaction_count() == 0) { - runtime_syslog(LOG_INFO, "Exiting cleanly."); + launchd_syslog(LOG_PERF, "Exiting cleanly."); } - - runtime_closelog(); - _vproc_transaction_end(); - #endif + + vproc_transaction_end(NULL, NULL); +#endif } - + runtime_busy_cnt--; + launchd_syslog(LOG_PERF, "Decremented busy count. Now: %lu", runtime_busy_cnt); runtime_install_timer(); } @@ -1577,9 +1127,9 @@ void runtime_add_weak_ref(void) { if (!pid1_magic) { - #if !TARGET_OS_EMBEDDED +#if !TARGET_OS_EMBEDDED _vproc_standby_begin(); - #endif +#endif } runtime_standby_cnt++; } @@ -1588,9 +1138,9 @@ void runtime_del_weak_ref(void) { if (!pid1_magic) { - #if !TARGET_OS_EMBEDDED +#if !TARGET_OS_EMBEDDED _vproc_standby_end(); - #endif +#endif } runtime_standby_cnt--; } @@ -1599,7 +1149,8 @@ void runtime_install_timer(void) { if (!pid1_magic && runtime_busy_cnt == 0) { - (void)launchd_assumes(kevent_mod((uintptr_t)&g_runtime_busy_time, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 30, root_jobmgr) != -1); + launchd_syslog(LOG_PERF, "Gone idle. Installing idle-exit timer."); + (void)posix_assumes_zero(kevent_mod((uintptr_t)&launchd_runtime_busy_time, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 10, root_jobmgr)); } } @@ -1607,7 +1158,10 @@ void runtime_remove_timer(void) { if (!pid1_magic && runtime_busy_cnt > 0) { - (void)launchd_assumes(kevent_mod((uintptr_t)&g_runtime_busy_time, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1); + if (runtime_busy_cnt == 1) { + launchd_syslog(LOG_PERF, "No longer idle. Removing idle-exit timer."); + } + (void)posix_assumes_zero(kevent_mod((uintptr_t)&launchd_runtime_busy_time, EVFILT_TIMER, EV_DELETE, 0, 0, NULL)); } } @@ -1617,13 +1171,13 @@ catch_mach_exception_raise(mach_port_t exception_port __attribute__((unused)), m { pid_t p4t = -1; - (void)launchd_assumes(pid_for_task(task, &p4t) == 0); - - runtime_syslog(LOG_NOTICE, "%s(): PID: %u thread: 0x%x type: 0x%x code: %p codeCnt: 0x%x", + (void)osx_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)launchd_assumes(launchd_mport_deallocate(thread) == KERN_SUCCESS); - (void)launchd_assumes(launchd_mport_deallocate(task) == KERN_SUCCESS); + + (void)osx_assumes_zero(launchd_mport_deallocate(thread)); + (void)osx_assumes_zero(launchd_mport_deallocate(task)); return KERN_SUCCESS; } @@ -1634,9 +1188,9 @@ catch_mach_exception_raise_state(mach_port_t exception_port __attribute__((unuse int *flavor, const thread_state_t old_state, mach_msg_type_number_t old_stateCnt, thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) { - runtime_syslog(LOG_NOTICE, "%s(): type: 0x%x code: %p codeCnt: 0x%x flavor: %p old_state: %p old_stateCnt: 0x%x new_state: %p new_stateCnt: %p", + launchd_syslog(LOG_NOTICE, "%s(): type: 0x%x code: %p codeCnt: 0x%x flavor: %p old_state: %p old_stateCnt: 0x%x new_state: %p new_stateCnt: %p", __func__, exception, code, codeCnt, flavor, old_state, old_stateCnt, new_state, new_stateCnt); - + memcpy(new_state, old_state, old_stateCnt * sizeof(old_state[0])); *new_stateCnt = old_stateCnt; @@ -1651,16 +1205,16 @@ catch_mach_exception_raise_state_identity(mach_port_t exception_port __attribute { pid_t p4t = -1; - (void)launchd_assumes(pid_for_task(task, &p4t) == 0); + (void)osx_assumes_zero(pid_for_task(task, &p4t)); - runtime_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", + 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); - + memcpy(new_state, old_state, old_stateCnt * sizeof(old_state[0])); *new_stateCnt = old_stateCnt; - (void)launchd_assumes(launchd_mport_deallocate(thread) == KERN_SUCCESS); - (void)launchd_assumes(launchd_mport_deallocate(task) == KERN_SUCCESS); + (void)osx_assumes_zero(launchd_mport_deallocate(thread)); + (void)osx_assumes_zero(launchd_mport_deallocate(task)); return KERN_SUCCESS; } @@ -1676,14 +1230,16 @@ launchd_log_vm_stats(void) statsp = did_first_pass ? &stats : &orig_stats; - if (!launchd_assumes(host_statistics(mhs, HOST_VM_INFO, (host_info_t)statsp, &count) == KERN_SUCCESS)) { + if (osx_assumes_zero(host_statistics(mhs, HOST_VM_INFO, (host_info_t)statsp, &count)) != KERN_SUCCESS) { return; } - (void)launchd_assumes(count == HOST_VM_INFO_COUNT); + if (count != HOST_VM_INFO_COUNT) { + (void)osx_assumes_zero(count); + } if (did_first_pass) { - runtime_syslog(LOG_DEBUG, "VM statistics (now - orig): Free: %d Active: %d Inactive: %d Reactivations: %d PageIns: %d PageOuts: %d Faults: %d COW-Faults: %d Purgeable: %d Purges: %d", + launchd_syslog(LOG_DEBUG, "VM statistics (now - orig): Free: %d Active: %d Inactive: %d Reactivations: %d PageIns: %d PageOuts: %d Faults: %d COW-Faults: %d Purgeable: %d Purges: %d", stats.free_count - orig_stats.free_count, stats.active_count - orig_stats.active_count, stats.inactive_count - orig_stats.inactive_count, @@ -1695,7 +1251,7 @@ launchd_log_vm_stats(void) stats.purgeable_count - orig_stats.purgeable_count, stats.purges - orig_stats.purges); } else { - runtime_syslog(LOG_DEBUG, "VM statistics (now): Free: %d Active: %d Inactive: %d Reactivations: %d PageIns: %d PageOuts: %d Faults: %d COW-Faults: %d Purgeable: %d Purges: %d", + launchd_syslog(LOG_DEBUG, "VM statistics (now): Free: %d Active: %d Inactive: %d Reactivations: %d PageIns: %d PageOuts: %d Faults: %d COW-Faults: %d Purgeable: %d Purges: %d", orig_stats.free_count, orig_stats.active_count, orig_stats.inactive_count, @@ -1719,7 +1275,7 @@ runtime_get_wall_time(void) struct timeval tv; int64_t r; - (void)launchd_assumes(gettimeofday(&tv, NULL) != -1); + (void)posix_assumes_zero(gettimeofday(&tv, NULL)); r = tv.tv_sec; r *= USEC_PER_SEC; @@ -1781,58 +1337,58 @@ do_file_init(void) { struct stat sb; - launchd_assert(mach_timebase_info(&tbi) == 0); + osx_assert_zero(mach_timebase_info(&tbi)); tbi_float_val = tbi.numer; tbi_float_val /= tbi.denom; tbi_safe_math_max = UINT64_MAX / tbi.numer; + launchd_system_start = runtime_get_wall_time(); + if (getpid() == 1) { pid1_magic = true; } if (stat("/AppleInternal", &sb) == 0 && stat("/var/db/disableAppleInternal", &sb) == -1) { - do_apple_internal_logging = true; + launchd_apple_internal = true; } - if (stat("/var/db/.debug_launchd", &sb) == 0) { - internal_mask_pri = LOG_UPTO(LOG_DEBUG); - low_level_debug = true; + if (config_check(".launchd_use_gmalloc", sb)) { + launchd_use_gmalloc = true; } - - if (stat("/var/db/.launchd_log_per_user_shutdown", &sb) == 0) { - g_log_per_user_shutdown = true; + + if (config_check(".launchd_log_shutdown", sb)) { + launchd_log_shutdown = true; } - - if (stat("/var/db/.launchd_use_gmalloc", &sb) == 0) { - g_use_gmalloc = true; + + if (config_check(".launchd_log_debug", sb)) { + launchd_log_debug = true; } - if (stat("/var/db/.launchd_malloc_log_stacks", &sb) == 0) { - g_malloc_log_stacks = true; - g_use_gmalloc = false; + if (config_check(".launchd_log_perf", sb)) { + launchd_log_perf = true; } - - if (pid1_magic && stat("/var/db/.launchd_log_pid1_shutdown", &sb) == 0) { - g_log_pid1_shutdown = true; + + if (config_check("/etc/rc.cdrom", sb)) { + launchd_osinstaller = true; } - - char bootargs[128]; + + if (!pid1_magic && config_check(".launchd_allow_global_dyld_envvars", sb)) { + 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; if (r == 0) { if (strnstr(bootargs, "-v", len)) { - g_verbose_boot = true; + launchd_verbose_boot = true; } if (strnstr(bootargs, "launchd_trap_sigkill_bugs", len)) { - g_trap_sigkill_bugs = true; + launchd_trap_sigkill_bugs = true; } } - - if (pid1_magic && g_verbose_boot && stat("/var/db/.launchd_shutdown_debugging", &sb) == 0) { - g_shutdown_debugging = true; - } - - if (stat("/var/db/.launchd_log_strict_usage", &sb) == 0) { - g_log_strict_usage = true; + + if (pid1_magic && launchd_verbose_boot && config_check(".launchd_shutdown_debugging", sb)) { + launchd_shutdown_debugging = true; } } diff --git a/src/runtime.h b/src/runtime.h new file mode 100644 index 0000000..2b8e985 --- /dev/null +++ b/src/runtime.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +#ifndef __LAUNCHD_RUNTIME_H__ +#define __LAUNCHD_RUNTIME_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kill2.h" +#include "ktrace.h" +#include "log.h" + +#define likely(x) __builtin_expect((bool)(x), true) +#define unlikely(x) __builtin_expect((bool)(x), false) + +struct ldcred { + uid_t euid; + uid_t uid; + gid_t egid; + gid_t gid; + pid_t pid; + au_asid_t asid; + mach_port_t asport; +}; + +typedef void (*kq_callback)(void *, struct kevent *); +typedef boolean_t (*mig_callback)(mach_msg_header_t *, mach_msg_header_t *); +typedef void (*timeout_callback)(void); + +extern bool launchd_verbose_boot; +/* Configuration knobs set in do_file_init(). */ +extern bool launchd_shutdown_debugging; +extern bool launchd_use_gmalloc; +extern bool launchd_malloc_log_stacks; +extern bool launchd_log_shutdown; +extern bool launchd_log_debug; +extern bool launchd_log_perf; +extern bool launchd_trap_sigkill_bugs; +extern bool launchd_osinstaller; +extern bool launchd_allow_global_dyld_envvars; + +extern bool launchd_runtime_busy_time; +extern mach_port_t inherited_bootstrap_port; +extern size_t runtime_busy_cnt; +extern int32_t launchd_sync_frequency; +extern pid_t launchd_wsp; + +mach_port_t runtime_get_kernel_port(void); +extern boolean_t launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply); + +void runtime_add_ref(void); +void runtime_del_ref(void); +void runtime_add_weak_ref(void); +void runtime_del_weak_ref(void); +void runtime_install_timer(void); +void runtime_remove_timer(void); + +void launchd_runtime_init(void); +void launchd_runtime_init2(void); +void launchd_runtime(void) __attribute__((noreturn)); + +void launchd_log_vm_stats(void); + +int runtime_close(int fd); +int runtime_fsync(int fd); + +#define RUNTIME_ADVISABLE_IDLE_TIMEOUT 30 + +void runtime_set_timeout(timeout_callback to_cb, unsigned int sec); +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); + +const char *signal_to_C_name(unsigned int sig); +const char *reboot_flags_to_C_names(unsigned int flags); + +int kevent_bulk_mod(struct kevent *kev, size_t kev_cnt); +int kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata); +void log_kevent_struct(int level, struct kevent *kev_base, int indx); + +pid_t runtime_fork(mach_port_t bsport); + +mach_msg_return_t launchd_exc_runtime_once(mach_port_t port, mach_msg_size_t rcv_msg_size, mach_msg_size_t send_msg_size, mig_reply_error_t *bufRequest, mig_reply_error_t *bufReply, mach_msg_timeout_t to); + +int64_t runtime_get_wall_time(void) __attribute__((warn_unused_result)); +uint64_t runtime_get_opaque_time(void) __attribute__((warn_unused_result)); +uint64_t runtime_get_opaque_time_of_event(void) __attribute__((pure, warn_unused_result)); +uint64_t runtime_opaque_time_to_nano(uint64_t o) __attribute__((const, warn_unused_result)); +uint64_t runtime_get_nanoseconds_since(uint64_t o) __attribute__((pure, warn_unused_result)); + +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); +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); + +#endif /* __LAUNCHD_RUNTIME_H__ */ diff --git a/launchd/src/launchctl.c b/support/launchctl.c similarity index 68% rename from launchd/src/launchctl.c rename to support/launchctl.c index 8510c9f..9e74f83 100644 --- a/launchd/src/launchctl.c +++ b/support/launchctl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2010 Apple Inc. All rights reserved. + * Copyright (c) 2005-2011 Apple Inc. All rights reserved. * * @APPLE_APACHE_LICENSE_HEADER_START@ * @@ -18,8 +18,6 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ -static const char *const __rcs_file_version__ = "$Revision: 25957 $"; - #include "config.h" #include "launch.h" #include "launch_priv.h" @@ -33,7 +31,6 @@ static const char *const __rcs_file_version__ = "$Revision: 25957 $"; #include #include #include -#include #include #include #include @@ -44,10 +41,6 @@ static const char *const __rcs_file_version__ = "$Revision: 25957 $"; #include #include #include -#ifndef SO_EXECPATH -/* This is just so it's easy for me to compile launchctl without buildit. */ - #define SO_EXECPATH 0x1085 -#endif #include #include #include @@ -82,6 +75,8 @@ static const char *const __rcs_file_version__ = "$Revision: 25957 $"; #include #include #include +#include +#include #if HAVE_LIBAUDITD #include @@ -92,36 +87,31 @@ static const char *const __rcs_file_version__ = "$Revision: 25957 $"; extern char **environ; - #define LAUNCH_SECDIR _PATH_TMP "launch-XXXXXX" #define LAUNCH_ENV_KEEPCONTEXT "LaunchKeepContext" - -#define MACHINIT_JOBKEY_ONDEMAND "OnDemand" -#define MACHINIT_JOBKEY_SERVICENAME "ServiceName" -#define MACHINIT_JOBKEY_COMMAND "Command" -#define MACHINIT_JOBKEY_SERVERPORT "ServerPort" -#define MACHINIT_JOBKEY_SERVICEPORT "ServicePort" - -#define assumes(e) \ - (__builtin_expect(!(e), 0) ? _log_launchctl_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true) +#define LAUNCH_ENV_BOOTSTRAPPINGSYSTEM "LaunchBootstrappingSystem" #define CFTypeCheck(cf, type) (CFGetTypeID(cf) == type ## GetTypeID()) +#define CFReleaseIfNotNULL(cf) if (cf) CFRelease(cf); struct load_unload_state { launch_data_t pass1; - launch_data_t pass2; char *session_type; bool editondisk:1, load:1, forceload:1; }; +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); static bool launch_data_array_append(launch_data_t a, launch_data_t o); +static void insert_event(launch_data_t, const char *, const char *, launch_data_t); static void distill_jobs(launch_data_t); static void distill_config_file(launch_data_t); +static void distill_fsevents(launch_data_t); 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); @@ -134,14 +124,11 @@ static void readpath(const char *, struct load_unload_state *); static void readfile(const char *, struct load_unload_state *); static int _fd(int); static int demux_cmd(int argc, char *const argv[]); -static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv); static void submit_job_pass(launch_data_t jobs); static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup); static mach_port_t str2bsport(const char *s); static void print_jobs(launch_data_t j, const char *key, void *context); static void print_obj(launch_data_t obj, const char *key, void *context); -static bool delay_to_second_pass(launch_data_t o); -static void delay_to_second_pass2(launch_data_t o, const char *key, void *context); static bool str2lim(const char *buf, rlim_t *res); static const char *lim2str(rlim_t val, char *buf); static const char *num2name(int n); @@ -149,7 +136,6 @@ static ssize_t name2num(const char *n); static void unloadjob(launch_data_t job); static void print_key_value(launch_data_t obj, const char *key, void *context); static void print_launchd_env(launch_data_t obj, const char *key, void *context); -static void _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test); static void loopback_setup_ipv4(void); static void loopback_setup_ipv6(void); static pid_t fwexec(const char *const *argv, int *wstatus); @@ -169,7 +155,6 @@ static bool do_single_user_mode2(void); static void do_crash_debug_mode(void); static bool do_crash_debug_mode2(void); static void read_launchd_conf(void); -static void read_environment_dot_plist(void); static bool job_disabled_logic(launch_data_t obj); static void fix_bogus_file_metadata(void); static void do_file_init(void) __attribute__((constructor)); @@ -254,111 +239,116 @@ static const struct { { "help", help_cmd, "This help output" }, }; -static bool istty; -static bool verbose; -static bool is_managed; -static bool do_apple_internal_magic; -static bool system_context; -static bool rootuser_context; -static bool bootstrapping_system; -static bool bootstrapping_peruser; -static bool g_verbose_boot = false; -static bool g_startup_debugging = false; - -static bool g_job_overrides_db_has_changed = false; -static CFMutableDictionaryRef g_job_overrides_db = NULL; -static char g_job_overrides_db_path[PATH_MAX]; +static bool _launchctl_istty; +static bool _launchctl_verbose; +static bool _launchctl_is_managed; +static bool _launchctl_apple_internal; +static bool _launchctl_system_context; +static bool _launchctl_uid0_context; +static bool _launchctl_system_bootstrap; +static bool _launchctl_peruser_bootstrap; +static bool _launchctl_verbose_boot = false; +static bool _launchctl_startup_debugging = false; -#if 0 -static bool g_job_cache_db_has_changed = false; -static launch_data_t g_job_cache_db = NULL; -static char g_job_cache_db_path[PATH_MAX]; -#endif +static bool _launchctl_overrides_db_changed = false; +static CFMutableDictionaryRef _launchctl_overrides_db = NULL; + +static char *_launchctl_job_overrides_db_path; +static char *_launchctl_managername = NULL; int main(int argc, char *const argv[]) { - int64_t is_managed_val = 0; char *l; - if (vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed_val) == NULL && is_managed_val) { - is_managed = true; + if (getenv(LAUNCH_ENV_BOOTSTRAPPINGSYSTEM)) { + /* We're bootstrapping the install environment, so we can't talk to + * mDNSResponder or opendirectoryd. + * + * See . + */ + si_search_module_set_flags("mdns", 1); + si_search_module_set_flags("ds", 1); } - - istty = isatty(STDIN_FILENO); + + int64_t is_managed = 0; + (void)vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed); + _launchctl_is_managed = is_managed; + + _launchctl_istty = isatty(STDIN_FILENO); argc--, argv++; - + if (argc > 0 && argv[0][0] == '-') { char *flago; for (flago = argv[0] + 1; *flago; flago++) { switch (*flago) { case 'v': - verbose = true; + _launchctl_verbose = true; break; case 'u': if (argc > 1) { if (strncmp(argv[1], "root", sizeof("root")) == 0) { - rootuser_context = true; + _launchctl_uid0_context = true; } else { - fprintf(stderr, "Unknown user: %s\n", argv[1]); + launchctl_log(LOG_ERR, "Unknown user: %s", argv[1]); exit(EXIT_FAILURE); } argc--, argv++; } else { - fprintf(stderr, "-u option requires an argument.\n"); + launchctl_log(LOG_ERR, "-u option requires an argument."); } break; case '1': - system_context = true; + _launchctl_system_context = true; break; default: - fprintf(stderr, "Unknown argument: '-%c'\n", *flago); + launchctl_log(LOG_ERR, "Unknown argument: '-%c'", *flago); break; } } argc--, argv++; } - /* Running in the context of the root user's per-user launchd is only supported ... well - * in the root user's per-user context. I know it's confusing. I'm genuinely sorry. + /* Running in the context of the root user's per-user launchd is only from + * within that session. */ - if (rootuser_context) { + if (_launchctl_uid0_context) { int64_t manager_uid = -1, manager_pid = -1; - if (vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, &manager_uid) == NULL) { - if (vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, &manager_pid) == NULL) { - if (manager_uid || manager_pid == 1) { - fprintf(stderr, "Running in the root user's per-user context is not supported outside of the root user's bootstrap.\n"); - exit(EXIT_FAILURE); - } - } + (void)vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, &manager_uid); + (void)vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, &manager_pid); + if (manager_uid || manager_pid == 1) { + launchctl_log(LOG_ERR, "Running in the root user's per-user context is not supported outside of the root user's bootstrap."); + exit(EXIT_FAILURE); } - } else if (!(system_context || rootuser_context)) { - /* Running in the system context is implied when we're running as root and not running as a bootstrapper. */ - system_context = (!is_managed && getuid() == 0); + } else if (!(_launchctl_system_context || _launchctl_uid0_context)) { + /* Running in the system context is implied when we're running as root + * and not running as a bootstrapper. + */ + _launchctl_system_context = (!_launchctl_is_managed && getuid() == 0); } - if (system_context) { + if (_launchctl_system_context) { if (getuid() == 0) { setup_system_context(); } else { - fprintf(stderr, "You must be root to run in the system context.\n"); + launchctl_log(LOG_ERR, "You must be root to run in the system context."); exit(EXIT_FAILURE); } - } else if (rootuser_context) { + } else if (_launchctl_uid0_context) { if (getuid() != 0) { - fprintf(stderr, "You must be root to run in the root user context.\n"); + launchctl_log(LOG_ERR, "You must be root to run in the root user context."); exit(EXIT_FAILURE); } } - - if (NULL == readline) { - fprintf(stderr, "missing library: readline\n"); + + if (!readline) { + launchctl_log(LOG_ERR, "missing library: readline"); exit(EXIT_FAILURE); } if (argc == 0) { - while ((l = readline(istty ? "launchd% " : NULL))) { + while ((l = readline(_launchctl_istty ? "launchd% " : NULL))) { char *inputstring = l, *argv2[100], **ap = argv2; int i = 0; @@ -376,7 +366,7 @@ main(int argc, char *const argv[]) free(l); } - if (istty) { + if (_launchctl_istty) { fputc('\n', stdout); } } @@ -395,17 +385,51 @@ demux_cmd(int argc, char *const argv[]) optind = 1; optreset = 1; - + for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) { if (!strcmp(cmds[i].name, argv[0])) { return cmds[i].func(argc, argv); } } - fprintf(stderr, "%s: unknown subcommand \"%s\"\n", getprogname(), argv[0]); + launchctl_log(LOG_ERR, "%s: unknown subcommand \"%s\"", getprogname(), argv[0]); return 1; } +void +launchctl_log(int level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + if (_launchctl_is_managed) { + vsyslog(level, fmt, ap); + } else { + char *buff = NULL; + (void)vasprintf(&buff, fmt, ap); + + FILE *where = stdout; + if (level < LOG_NOTICE) { + where = stderr; + } + + fprintf(where, "%s\n", buff); + free(buff); + } + + va_end(ap); +} + +void +launchctl_log_CFString(int level, CFStringRef string) +{ + // Big enough. Don't feel like jumping through CF's hoops. + char *buff = malloc(4096); + (void)CFStringGetCString(string, buff, 4096, kCFStringEncodingUTF8); + launchctl_log(level, "%s", buff); + free(buff); +} + void read_launchd_conf(void) { @@ -447,122 +471,37 @@ read_launchd_conf(void) fclose(f); } -CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL) +static CFPropertyListRef +CFPropertyListCreateFromFile(CFURLRef plistURL) { CFReadStreamRef plistReadStream = CFReadStreamCreateWithFile(NULL, plistURL); - + CFErrorRef streamErr = NULL; if (!CFReadStreamOpen(plistReadStream)) { streamErr = CFReadStreamCopyError(plistReadStream); CFStringRef errString = CFErrorCopyDescription(streamErr); - - CFShow(errString); - + + launchctl_log_CFString(LOG_ERR, errString); + CFRelease(errString); CFRelease(streamErr); } - + CFPropertyListRef plist = NULL; if (plistReadStream) { CFStringRef errString = NULL; CFPropertyListFormat plistFormat = 0; plist = CFPropertyListCreateFromStream(NULL, plistReadStream, 0, kCFPropertyListImmutable, &plistFormat, &errString); if (!plist) { - CFShow(errString); + launchctl_log_CFString(LOG_ERR, errString); CFRelease(errString); } } - + CFReadStreamClose(plistReadStream); CFRelease(plistReadStream); - - return plist; -} - -static void -sanitize_environment_dot_plist(const launch_data_t val, const char *key, void *ctx) -{ - launch_data_t copy = (launch_data_t)ctx; - if (strncmp("DYLD_", key, sizeof("DYLD_") - 1) != 0) { - launch_data_t copyi = launch_data_copy(val); - launch_data_dict_insert(copy, copyi, key); - } -} -#define CFReleaseIfNotNULL(cf) if (cf) CFRelease(cf); -void -read_environment_dot_plist(void) -{ - CFStringRef plistPath = NULL; - CFURLRef plistURL = NULL; - CFDictionaryRef envPlist = NULL; - launch_data_t req = NULL, launch_env_dict = NULL, resp = NULL; - - char plist_path_str[PATH_MAX]; - plist_path_str[PATH_MAX - 1] = 0; - snprintf(plist_path_str, sizeof(plist_path_str), "%s/.MacOSX/environment.plist", getenv("HOME")); - - struct stat sb; - if (stat(plist_path_str, &sb) == -1) { - goto out; - } - - plistPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), plist_path_str); - if (!assumes(plistPath != NULL)) { - goto out; - } - - plistURL = CFURLCreateWithFileSystemPath(NULL, plistPath, kCFURLPOSIXPathStyle, false); - if (!assumes(plistURL != NULL)) { - goto out; - } - - envPlist = (CFDictionaryRef)CFPropertyListCreateFromFile(plistURL); - if (!assumes(envPlist != NULL)) { - goto out; - } - - launch_env_dict = CF2launch_data(envPlist); - if (!assumes(launch_env_dict != NULL)) { - goto out; - } - - launch_data_t sanitized = launch_data_alloc(LAUNCH_DATA_DICTIONARY); - if (!assumes(sanitized != NULL)) { - goto out; - } - - launch_data_dict_iterate(launch_env_dict, sanitize_environment_dot_plist, sanitized); - launch_data_free(launch_env_dict); - - req = launch_data_alloc(LAUNCH_DATA_DICTIONARY); - if (!assumes(req != NULL)) { - goto out; - } - - launch_data_dict_insert(req, sanitized, LAUNCH_KEY_SETUSERENVIRONMENT); - - resp = launch_msg(req); - if (!assumes(resp != NULL)) { - goto out; - } - - if (!assumes(launch_data_get_type(resp) == LAUNCH_DATA_ERRNO)) { - goto out; - } - - (void)assumes(launch_data_get_errno(resp) == 0); -out: - CFReleaseIfNotNULL(plistPath); - CFReleaseIfNotNULL(plistURL); - CFReleaseIfNotNULL(envPlist); - if (req) { - launch_data_free(req); - } - - if (resp) { - launch_data_free(resp); - } + return plist; } int @@ -571,7 +510,7 @@ unsetenv_cmd(int argc, char *const argv[]) launch_data_t resp, tmp, msg; if (argc != 2) { - fprintf(stderr, "%s usage: unsetenv \n", getprogname()); + launchctl_log(LOG_ERR, "%s usage: unsetenv ", getprogname()); return 1; } @@ -587,7 +526,7 @@ unsetenv_cmd(int argc, char *const argv[]) if (resp) { launch_data_free(resp); } else { - fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_UNSETUSERENVIRONMENT, strerror(errno)); + launchctl_log(LOG_ERR, "launch_msg(\"%s\"): %s", LAUNCH_KEY_UNSETUSERENVIRONMENT, strerror(errno)); } return 0; @@ -599,7 +538,7 @@ setenv_cmd(int argc, char *const argv[]) launch_data_t resp, tmp, tmpv, msg; if (argc != 3) { - fprintf(stderr, "%s usage: setenv \n", getprogname()); + launchctl_log(LOG_ERR, "%s usage: setenv ", getprogname()); return 1; } @@ -616,7 +555,7 @@ setenv_cmd(int argc, char *const argv[]) if (resp) { launch_data_free(resp); } else { - fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_SETUSERENVIRONMENT, strerror(errno)); + launchctl_log(LOG_ERR, "launch_msg(\"%s\"): %s", LAUNCH_KEY_SETUSERENVIRONMENT, strerror(errno)); } return 0; @@ -629,9 +568,9 @@ print_launchd_env(launch_data_t obj, const char *key, void *context) /* XXX escape the double quotes */ if (*is_csh) { - fprintf(stdout, "setenv %s \"%s\";\n", key, launch_data_get_string(obj)); + launchctl_log(LOG_NOTICE, "setenv %s \"%s\";", key, launch_data_get_string(obj)); } else { - fprintf(stdout, "%s=\"%s\"; export %s;\n", key, launch_data_get_string(obj), key); + launchctl_log(LOG_NOTICE, "%s=\"%s\"; export %s;", key, launch_data_get_string(obj), key); } } @@ -641,7 +580,7 @@ print_key_value(launch_data_t obj, const char *key, void *context) const char *k = context; if (!strcmp(key, k)) { - fprintf(stdout, "%s\n", launch_data_get_string(obj)); + launchctl_log(LOG_NOTICE, "%s", launch_data_get_string(obj)); } } @@ -651,14 +590,14 @@ getenv_and_export_cmd(int argc, char *const argv[]) launch_data_t resp; bool is_csh = false; char *k; - + if (!strcmp(argv[0], "export")) { char *s = getenv("SHELL"); if (s) { is_csh = strstr(s, "csh") ? true : false; } } else if (argc != 2) { - fprintf(stderr, "%s usage: getenv \n", getprogname()); + launchctl_log(LOG_ERR, "%s usage: getenv ", getprogname()); return 1; } @@ -683,10 +622,10 @@ int wait4debugger_cmd(int argc, char * const argv[]) { if (argc != 3) { - fprintf(stderr, "%s usage: debug