diff --git a/src/libnotify/Libnotify.xcodeproj/project.pbxproj b/src/libnotify/Libnotify.xcodeproj/project.pbxproj index ae4470aed..0cdd356fb 100644 --- a/src/libnotify/Libnotify.xcodeproj/project.pbxproj +++ b/src/libnotify/Libnotify.xcodeproj/project.pbxproj @@ -20,16 +20,27 @@ name = cli_apps; productName = cli_apps; }; + 72FA84FB1BD6EAB900A4CC6F /* tests */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 72FA84FC1BD6EAB900A4CC6F /* Build configuration list for PBXAggregateTarget "tests" */; + buildPhases = ( + ); + dependencies = ( + 72FA84FF1BD6EC2000A4CC6F /* PBXTargetDependency */, + ); + name = tests; + productName = tests; + }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 1886391920E1A2BF00C8BEA9 /* notify_bench.c in Sources */ = {isa = PBXBuildFile; fileRef = 727C90371B9A372700D5B754 /* notify_bench.c */; }; 2D312B76102CA2E300F90022 /* libnotify.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D312B73102CA2E300F90022 /* libnotify.c */; }; 2D312B77102CA2E300F90022 /* notify_client.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D312B74102CA2E300F90022 /* notify_client.c */; }; 2D312B78102CA2E300F90022 /* table.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D312B75102CA2E300F90022 /* table.c */; }; 2D312B7A102CA30200F90022 /* notify_ipc.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2D312B79102CA30200F90022 /* notify_ipc.defs */; settings = {ATTRIBUTES = (Client, ); }; }; 2D312B7E102CA32500F90022 /* notify_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D312B7C102CA32500F90022 /* notify_keys.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2D312B7F102CA32500F90022 /* notify.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D312B7D102CA32500F90022 /* notify.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D312B82102CA34D00F90022 /* libnotify.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D312B81102CA34D00F90022 /* libnotify.h */; settings = {ATTRIBUTES = (Private, ); }; }; 2D312B87102CA36C00F90022 /* table.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D312B85102CA36C00F90022 /* table.h */; }; 2D38AA0A102CD88300D3D622 /* notify.3 in Copy man3 Files */ = {isa = PBXBuildFile; fileRef = 2D312B92102CA38F00F90022 /* notify.3 */; }; 2D38AA0B102CD89B00D3D622 /* notify_cancel.3 in Copy man3 Files */ = {isa = PBXBuildFile; fileRef = 2D312B88102CA38F00F90022 /* notify_cancel.3 */; }; @@ -43,6 +54,7 @@ 2D38AA13102CD8B800D3D622 /* notify_register_mach_port.3 in Copy man3 Files */ = {isa = PBXBuildFile; fileRef = 2D312B8F102CA38F00F90022 /* notify_register_mach_port.3 */; }; 2D38AA14102CD8B800D3D622 /* notify_register_signal.3 in Copy man3 Files */ = {isa = PBXBuildFile; fileRef = 2D312B90102CA38F00F90022 /* notify_register_signal.3 */; }; 2D38AA15102CD8B800D3D622 /* notify_set_state.3 in Copy man3 Files */ = {isa = PBXBuildFile; fileRef = 2D312B91102CA38F00F90022 /* notify_set_state.3 */; }; + 2D4F060B1B14E7DF00D59CEF /* com.apple.notifyd.sb in Install Sandbox profile */ = {isa = PBXBuildFile; fileRef = 2DD962061B14E6560040D341 /* com.apple.notifyd.sb */; }; 2DCB287210D99ADA00DF3A8D /* notify_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DCB287110D99ADA00DF3A8D /* notify_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3FA21ACF148AAA5000099D2F /* notify_proc.c in Sources */ = {isa = PBXBuildFile; fileRef = 3FA21A9C148AA7FA00099D2F /* notify_proc.c */; }; 3FA21AD0148AAA5000099D2F /* notifyd.c in Sources */ = {isa = PBXBuildFile; fileRef = 3FA21A9E148AA7FA00099D2F /* notifyd.c */; }; @@ -55,6 +67,16 @@ 3FA21AD8148AAABE00099D2F /* com.apple.notifyd.plist in Install launchd.plist */ = {isa = PBXBuildFile; fileRef = 3FA21A99148AA7FA00099D2F /* com.apple.notifyd.plist */; }; 3FA21AE6148AAEAC00099D2F /* notify_ipc.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2D312B79102CA30200F90022 /* notify_ipc.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; 3FD0DBAD148AB12000C50811 /* libbsm.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3FD0DBAC148AB12000C50811 /* libbsm.dylib */; }; + 6E8E6AD02282A7DA0084C085 /* notify.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6E8E6ACD2282A7DA0084C085 /* notify.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + 6EC91ED52178FF7100F11587 /* libCrashReporterClient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EC91ED42178FF7100F11587 /* libCrashReporterClient.a */; }; + 94099C762087E7D50004B6BC /* RegisterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 94099C752087E7D50004B6BC /* RegisterTests.m */; settings = {COMPILER_FLAGS = "-Wno-gnu-statement-expression -fobjc-exceptions"; }; }; + 9456B8522023CAB300CF7D27 /* libnotify.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D312B73102CA2E300F90022 /* libnotify.c */; }; + 9456B8532023CAB600CF7D27 /* table.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D312B75102CA2E300F90022 /* table.c */; }; + 9487BF2A2089A99A0043BF74 /* notify_client.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D312B74102CA2E300F90022 /* notify_client.c */; }; + 9487BF2B2089A9E30043BF74 /* notify_ipc.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2D312B79102CA30200F90022 /* notify_ipc.defs */; }; + 9487BF2C2089BAA30043BF74 /* table.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D312B75102CA2E300F90022 /* table.c */; }; + 9487BF2D2089BAB20043BF74 /* libnotify.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D312B73102CA2E300F90022 /* libnotify.c */; }; + E6481E7F2165785F00C04412 /* notify_probes.d in Sources */ = {isa = PBXBuildFile; fileRef = E6481E7E2165785F00C04412 /* notify_probes.d */; }; FC7B7A53155781930064D203 /* notify_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7B7A52155781930064D203 /* notify_internal.h */; }; /* End PBXBuildFile section */ @@ -73,13 +95,30 @@ remoteGlobalIDString = 3FA21ABD148AA8F000099D2F; remoteInfo = notifyutil; }; + 72FA84FE1BD6EC2000A4CC6F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 72FA84F61BD6E9DF00A4CC6F; + remoteInfo = darwintests; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 1886391220E1A22F00C8BEA9 /* Install man page */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + ); + name = "Install man page"; + runOnlyForDeploymentPostprocessing = 1; + }; 2D38AA09102CD87C00D3D622 /* Copy man3 Files */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man3"; + dstPath = /usr/share/man/man3; dstSubfolderSpec = 0; files = ( 2D38AA0A102CD88300D3D622 /* notify.3 in Copy man3 Files */, @@ -98,10 +137,21 @@ name = "Copy man3 Files"; runOnlyForDeploymentPostprocessing = 1; }; + 2D4F060A1B14E79900D59CEF /* Install Sandbox profile */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /System/Library/Sandbox/Profiles; + dstSubfolderSpec = 0; + files = ( + 2D4F060B1B14E7DF00D59CEF /* com.apple.notifyd.sb in Install Sandbox profile */, + ); + name = "Install Sandbox profile"; + runOnlyForDeploymentPostprocessing = 1; + }; 3FA21AAE148AA8E300099D2F /* Install man page */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; - dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man8"; + dstPath = /usr/share/man/man8; dstSubfolderSpec = 0; files = ( 3FA21AD4148AAA5D00099D2F /* notifyd.8 in Install man page */, @@ -112,7 +162,7 @@ 3FA21ABC148AA8F000099D2F /* Install man page */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; - dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man1"; + dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( 3FA21AD6148AAA7500099D2F /* notifyutil.1 in Install man page */, @@ -123,7 +173,7 @@ 3FA21AD7148AAAA600099D2F /* Install launchd.plist */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "$(INSTALL_PATH_PREFIX)/System/Library/LaunchDaemons"; + dstPath = /System/Library/LaunchDaemons; dstSubfolderSpec = 0; files = ( 3FA21AD8148AAABE00099D2F /* com.apple.notifyd.plist in Install launchd.plist */, @@ -134,15 +184,21 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 2D312B73102CA2E300F90022 /* libnotify.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libnotify.c; sourceTree = ""; }; - 2D312B74102CA2E300F90022 /* notify_client.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_client.c; sourceTree = ""; }; + 180F8D3E21136EBF009A472B /* notify_pathwatch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notify_pathwatch.c; sourceTree = ""; }; + 182FD2C2210BB0F600BF263C /* notify_benchmark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_benchmark.c; sourceTree = ""; }; + 18314CC821FF867600FEB43D /* notifyutil_entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = notifyutil_entitlements.plist; sourceTree = ""; }; + 18383C6720F909A300D5C465 /* notify_regenerate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_regenerate.c; sourceTree = ""; }; + 18383C6820F909A300D5C465 /* notify_register_signal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_register_signal.c; sourceTree = ""; }; + 18383C6920F909A300D5C465 /* notify_sigusr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_sigusr.c; sourceTree = ""; }; + 1886391720E1A22F00C8BEA9 /* notifybench */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = notifybench; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D312B73102CA2E300F90022 /* libnotify.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libnotify.c; sourceTree = ""; usesTabs = 1; }; + 2D312B74102CA2E300F90022 /* notify_client.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_client.c; sourceTree = ""; usesTabs = 1; }; 2D312B75102CA2E300F90022 /* table.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = table.c; sourceTree = ""; }; 2D312B79102CA30200F90022 /* notify_ipc.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = notify_ipc.defs; sourceTree = ""; }; 2D312B7C102CA32500F90022 /* notify_keys.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; fileEncoding = 4; path = notify_keys.h; sourceTree = ""; }; 2D312B7D102CA32500F90022 /* notify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = notify.h; sourceTree = ""; }; - 2D312B81102CA34D00F90022 /* libnotify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libnotify.h; sourceTree = ""; }; - 2D312B84102CA36C00F90022 /* notify_ipc_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = notify_ipc_types.h; sourceTree = ""; }; - 2D312B85102CA36C00F90022 /* table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = ""; }; + 2D312B81102CA34D00F90022 /* libnotify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libnotify.h; sourceTree = ""; usesTabs = 1; }; + 2D312B85102CA36C00F90022 /* table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = ""; usesTabs = 1; }; 2D312B88102CA38F00F90022 /* notify_cancel.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = notify_cancel.3; sourceTree = ""; }; 2D312B89102CA38F00F90022 /* notify_check.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = notify_check.3; sourceTree = ""; }; 2D312B8A102CA38F00F90022 /* notify_get_state.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = notify_get_state.3; sourceTree = ""; }; @@ -156,6 +212,7 @@ 2D312B92102CA38F00F90022 /* notify.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = notify.3; sourceTree = ""; }; 2D6D820D18DA602A0034E7B4 /* notify_is_valid_token.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = notify_is_valid_token.3; sourceTree = ""; }; 2DCB287110D99ADA00DF3A8D /* notify_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = notify_private.h; sourceTree = ""; }; + 2DD962061B14E6560040D341 /* com.apple.notifyd.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = com.apple.notifyd.sb; sourceTree = ""; }; 2DF9EA0B102CF33400DE9E8D /* APPLE_LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = APPLE_LICENSE; sourceTree = ""; }; 3F82235D12B18551005DD509 /* libnotify.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libnotify.xcconfig; sourceTree = ""; }; 3F8223B412B18877005DD509 /* libsystem_notify.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_notify.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -167,15 +224,14 @@ 3FA21A99148AA7FA00099D2F /* com.apple.notifyd.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = com.apple.notifyd.plist; sourceTree = ""; }; 3FA21A9A148AA7FA00099D2F /* notify.conf */ = {isa = PBXFileReference; lastKnownFileType = text; path = notify.conf; sourceTree = ""; }; 3FA21A9B148AA7FA00099D2F /* notify.conf.iPhone */ = {isa = PBXFileReference; lastKnownFileType = text; name = notify.conf.iPhone; path = ../notify.conf.iPhone; sourceTree = ""; }; - 3FA21A9C148AA7FA00099D2F /* notify_proc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notify_proc.c; sourceTree = ""; }; + 3FA21A9C148AA7FA00099D2F /* notify_proc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notify_proc.c; sourceTree = ""; usesTabs = 1; }; 3FA21A9D148AA7FA00099D2F /* notifyd.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = notifyd.8; sourceTree = ""; }; - 3FA21A9E148AA7FA00099D2F /* notifyd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notifyd.c; sourceTree = ""; }; - 3FA21A9F148AA7FA00099D2F /* notifyd.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = notifyd.h; sourceTree = ""; }; + 3FA21A9E148AA7FA00099D2F /* notifyd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notifyd.c; sourceTree = ""; usesTabs = 1; }; + 3FA21A9F148AA7FA00099D2F /* notifyd.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = notifyd.h; sourceTree = ""; usesTabs = 1; }; 3FA21AA0148AA7FA00099D2F /* pathwatch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pathwatch.c; sourceTree = ""; }; 3FA21AA1148AA7FA00099D2F /* pathwatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pathwatch.h; sourceTree = ""; }; - 3FA21AA2148AA7FA00099D2F /* service.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = service.c; sourceTree = ""; }; + 3FA21AA2148AA7FA00099D2F /* service.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = service.c; sourceTree = ""; usesTabs = 1; }; 3FA21AA3148AA7FA00099D2F /* service.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = service.h; sourceTree = ""; }; - 3FA21AA4148AA7FA00099D2F /* table.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = ""; }; 3FA21AA5148AA7FA00099D2F /* timer.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = timer.c; sourceTree = ""; }; 3FA21AA6148AA7FA00099D2F /* timer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = timer.h; sourceTree = ""; }; 3FA21AA8148AA82700099D2F /* notifyutil.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = notifyutil.1; sourceTree = ""; }; @@ -185,10 +241,34 @@ 3FA21ADD148AABA900099D2F /* mk_notify_conf.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = mk_notify_conf.sh; sourceTree = ""; }; 3FA21ADF148AACA000099D2F /* notify.conf.MacOSX */ = {isa = PBXFileReference; lastKnownFileType = text; name = notify.conf.MacOSX; path = notifyd/notify.conf.MacOSX; sourceTree = SOURCE_ROOT; }; 3FD0DBAC148AB12000C50811 /* libbsm.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbsm.dylib; path = /usr/lib/libbsm.dylib; sourceTree = ""; }; - FC7B7A52155781930064D203 /* notify_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = notify_internal.h; sourceTree = ""; }; + 6E8DAA2E2184283500A90CEA /* table.in.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = table.in.c; sourceTree = ""; usesTabs = 1; }; + 6E8E6ACC2282A7D30084C085 /* notify.defs */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.mig; name = notify.defs; path = usr/include/mach/notify.defs; sourceTree = SDKROOT; }; + 6E8E6ACD2282A7DA0084C085 /* notify.defs */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.mig; name = notify.defs; path = usr/include/mach/notify.defs; sourceTree = SDKROOT; }; + 6EC91ED42178FF7100F11587 /* libCrashReporterClient.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libCrashReporterClient.a; path = /usr/local/lib/libCrashReporterClient.a; sourceTree = ""; }; + 727C90301B9A372700D5B754 /* dispatch_cancel_in_block.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dispatch_cancel_in_block.c; sourceTree = ""; }; + 727C90341B9A372700D5B754 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + 727C90371B9A372700D5B754 /* notify_bench.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_bench.c; sourceTree = ""; }; + 727C90381B9A372700D5B754 /* notify_control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_control.c; sourceTree = ""; }; + 727C90391B9A372700D5B754 /* notify_disable_test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_disable_test.c; sourceTree = ""; }; + 727C903A1B9A372700D5B754 /* notify_many_dups.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_many_dups.c; sourceTree = ""; }; + 727C903B1B9A372700D5B754 /* random_test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = random_test.c; sourceTree = ""; }; + 94099C732087E7D50004B6BC /* xctests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = xctests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 94099C752087E7D50004B6BC /* RegisterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RegisterTests.m; sourceTree = ""; }; + 94099C772087E7D50004B6BC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9487BF2820882DAD0043BF74 /* parallel_register_cancel.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = parallel_register_cancel.c; sourceTree = ""; }; + 9B71D22F206AB52200BB9574 /* notify_qos.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_qos.c; sourceTree = ""; }; + E6481E7E2165785F00C04412 /* notify_probes.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; path = notify_probes.d; sourceTree = ""; }; + FC7B7A52155781930064D203 /* notify_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = notify_internal.h; sourceTree = ""; usesTabs = 1; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 1886391120E1A22F00C8BEA9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 3FA21AAD148AA8E300099D2F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -204,10 +284,18 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 94099C702087E7D50004B6BC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; D289987405E68DCB004EDB86 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 6EC91ED52178FF7100F11587 /* libCrashReporterClient.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -217,24 +305,41 @@ 08FB7794FE84155DC02AAC07 /* Libnotify */ = { isa = PBXGroup; children = ( + 1886391820E1A24A00C8BEA9 /* notifybench */, 3F94777F191C322100A93E8E /* xcodescripts */, 3F999960185C474E00EAD3A0 /* xcodeconfig */, 2D312B79102CA30200F90022 /* notify_ipc.defs */, + 6E8E6ACD2282A7DA0084C085 /* notify.defs */, 3FA21A97148AA7CD00099D2F /* libsystem_notify */, 3FA21AA7148AA82700099D2F /* notifyutil */, 3FA21A98148AA7FA00099D2F /* notifyd */, 2DF9EA0A102CF31700DE9E8D /* Additional Files */, + 94099C742087E7D50004B6BC /* xctests */, 3FA21ACE148AAA0D00099D2F /* Products */, + 727C902F1B9A372700D5B754 /* tests */, + 94099C732087E7D50004B6BC /* xctests.xctest */, + 1886391720E1A22F00C8BEA9 /* notifybench */, + 6EC91ED32178FF7100F11587 /* Frameworks */, ); name = Libnotify; sourceTree = ""; }; + 1886391820E1A24A00C8BEA9 /* notifybench */ = { + isa = PBXGroup; + children = ( + 727C90371B9A372700D5B754 /* notify_bench.c */, + ); + path = notifybench; + sourceTree = ""; + }; 2D312B72102CA2C400F90022 /* Source */ = { isa = PBXGroup; children = ( + E6481E7E2165785F00C04412 /* notify_probes.d */, 2D312B73102CA2E300F90022 /* libnotify.c */, 2D312B74102CA2E300F90022 /* notify_client.c */, 2D312B75102CA2E300F90022 /* table.c */, + 6E8DAA2E2184283500A90CEA /* table.in.c */, ); name = Source; sourceTree = ""; @@ -261,7 +366,6 @@ isa = PBXGroup; children = ( FC7B7A52155781930064D203 /* notify_internal.h */, - 2D312B84102CA36C00F90022 /* notify_ipc_types.h */, 2D312B85102CA36C00F90022 /* table.h */, ); name = "Project Headers"; @@ -287,6 +391,7 @@ 3F999960185C474E00EAD3A0 /* xcodeconfig */ = { isa = PBXGroup; children = ( + 6E8E6ACC2282A7D30084C085 /* notify.defs */, 3F999961185C474E00EAD3A0 /* base.xcconfig */, 3F82235D12B18551005DD509 /* libnotify.xcconfig */, 3F999963185C474E00EAD3A0 /* notifyd.xcconfig */, @@ -310,6 +415,7 @@ 3FA21A98148AA7FA00099D2F /* notifyd */ = { isa = PBXGroup; children = ( + 2DD962061B14E6560040D341 /* com.apple.notifyd.sb */, 3FA21ADC148AABA900099D2F /* Build Support */, 3FA21A99148AA7FA00099D2F /* com.apple.notifyd.plist */, 3FA21A9A148AA7FA00099D2F /* notify.conf */, @@ -325,6 +431,7 @@ children = ( 3FA21AA8148AA82700099D2F /* notifyutil.1 */, 3FA21AA9148AA82700099D2F /* notifyutil.c */, + 18314CC821FF867600FEB43D /* notifyutil_entitlements.plist */, ); path = notifyutil; sourceTree = ""; @@ -347,7 +454,6 @@ 3FA21A9F148AA7FA00099D2F /* notifyd.h */, 3FA21AA1148AA7FA00099D2F /* pathwatch.h */, 3FA21AA3148AA7FA00099D2F /* service.h */, - 3FA21AA4148AA7FA00099D2F /* table.h */, 3FA21AA6148AA7FA00099D2F /* timer.h */, ); name = "Private Headers"; @@ -375,6 +481,43 @@ path = xcodescripts; sourceTree = ""; }; + 6EC91ED32178FF7100F11587 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6EC91ED42178FF7100F11587 /* libCrashReporterClient.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 727C902F1B9A372700D5B754 /* tests */ = { + isa = PBXGroup; + children = ( + 18383C6720F909A300D5C465 /* notify_regenerate.c */, + 18383C6820F909A300D5C465 /* notify_register_signal.c */, + 18383C6920F909A300D5C465 /* notify_sigusr.c */, + 727C90341B9A372700D5B754 /* Makefile */, + 9B71D22F206AB52200BB9574 /* notify_qos.c */, + 727C90301B9A372700D5B754 /* dispatch_cancel_in_block.c */, + 727C90381B9A372700D5B754 /* notify_control.c */, + 727C90391B9A372700D5B754 /* notify_disable_test.c */, + 727C903A1B9A372700D5B754 /* notify_many_dups.c */, + 727C903B1B9A372700D5B754 /* random_test.c */, + 9487BF2820882DAD0043BF74 /* parallel_register_cancel.c */, + 182FD2C2210BB0F600BF263C /* notify_benchmark.c */, + 180F8D3E21136EBF009A472B /* notify_pathwatch.c */, + ); + path = tests; + sourceTree = ""; + }; + 94099C742087E7D50004B6BC /* xctests */ = { + isa = PBXGroup; + children = ( + 94099C752087E7D50004B6BC /* RegisterTests.m */, + 94099C772087E7D50004B6BC /* Info.plist */, + ); + path = xctests; + sourceTree = ""; + }; C6A0FF2B0290797F04C91782 /* Documentation */ = { isa = PBXGroup; children = ( @@ -403,7 +546,6 @@ files = ( 2D312B7E102CA32500F90022 /* notify_keys.h in Headers */, 2D312B7F102CA32500F90022 /* notify.h in Headers */, - 2D312B82102CA34D00F90022 /* libnotify.h in Headers */, 2D312B87102CA36C00F90022 /* table.h in Headers */, 2DCB287210D99ADA00DF3A8D /* notify_private.h in Headers */, FC7B7A53155781930064D203 /* notify_internal.h in Headers */, @@ -412,7 +554,41 @@ }; /* End PBXHeadersBuildPhase section */ +/* Begin PBXLegacyTarget section */ + 72FA84F61BD6E9DF00A4CC6F /* darwintests */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "$(ACTION)"; + buildConfigurationList = 72FA84F71BD6E9DF00A4CC6F /* Build configuration list for PBXLegacyTarget "darwintests" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/make; + buildWorkingDirectory = tests; + dependencies = ( + ); + name = darwintests; + passBuildSettingsInEnvironment = 1; + productName = darwintests; + }; +/* End PBXLegacyTarget section */ + /* Begin PBXNativeTarget section */ + 1886390E20E1A22F00C8BEA9 /* notifybench */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1886391420E1A22F00C8BEA9 /* Build configuration list for PBXNativeTarget "notifybench" */; + buildPhases = ( + 1886390F20E1A22F00C8BEA9 /* Sources */, + 1886391120E1A22F00C8BEA9 /* Frameworks */, + 1886391220E1A22F00C8BEA9 /* Install man page */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = notifybench; + productName = notifyutil; + productReference = 1886391720E1A22F00C8BEA9 /* notifybench */; + productType = "com.apple.product-type.tool"; + }; 3FA21AAF148AA8E300099D2F /* notifyd */ = { isa = PBXNativeTarget; buildConfigurationList = 3FA21AB8148AA8E300099D2F /* Build configuration list for PBXNativeTarget "notifyd" */; @@ -422,6 +598,7 @@ 3FA21AAE148AA8E300099D2F /* Install man page */, 3FA21AD7148AAAA600099D2F /* Install launchd.plist */, 3FA21ADB148AAB1C00099D2F /* Install notify.conf */, + 2D4F060A1B14E79900D59CEF /* Install Sandbox profile */, ); buildRules = ( ); @@ -449,6 +626,23 @@ productReference = 3FA21ABE148AA8F000099D2F /* notifyutil */; productType = "com.apple.product-type.tool"; }; + 94099C722087E7D50004B6BC /* xctests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 94099C792087E7D50004B6BC /* Build configuration list for PBXNativeTarget "xctests" */; + buildPhases = ( + 94099C6F2087E7D50004B6BC /* Sources */, + 94099C702087E7D50004B6BC /* Frameworks */, + 94099C712087E7D50004B6BC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = xctests; + productName = xctests; + productReference = 94099C732087E7D50004B6BC /* xctests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; D2AAC045055464E500DB518D /* libnotify */ = { isa = PBXNativeTarget; buildConfigurationList = 1DEB91EB08733DB70010E9CD /* Build configuration list for PBXNativeTarget "libnotify" */; @@ -475,6 +669,17 @@ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { + TargetAttributes = { + 72FA84F61BD6E9DF00A4CC6F = { + CreatedOnToolsVersion = 7.1; + }; + 72FA84FB1BD6EAB900A4CC6F = { + CreatedOnToolsVersion = 7.1; + }; + 94099C722087E7D50004B6BC = { + CreatedOnToolsVersion = 10.0; + }; + }; }; buildConfigurationList = 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "Libnotify" */; compatibilityVersion = "Xcode 3.1"; @@ -494,11 +699,25 @@ D2AAC045055464E500DB518D /* libnotify */, 3FA21AAF148AA8E300099D2F /* notifyd */, 3FA21ABD148AA8F000099D2F /* notifyutil */, + 1886390E20E1A22F00C8BEA9 /* notifybench */, 3FA21AC7148AA93000099D2F /* cli_apps */, + 72FA84F61BD6E9DF00A4CC6F /* darwintests */, + 72FA84FB1BD6EAB900A4CC6F /* tests */, + 94099C722087E7D50004B6BC /* xctests */, ); }; /* End PBXProject section */ +/* Begin PBXResourcesBuildPhase section */ + 94099C712087E7D50004B6BC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + /* Begin PBXShellScriptBuildPhase section */ 3F947782191C324900A93E8E /* No Simulator Man Pages */ = { isa = PBXShellScriptBuildPhase; @@ -558,18 +777,29 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = "/bin/bash -e -x"; - shellScript = "exec \"${SCRIPT_INPUT_FILE_0}\""; + shellScript = "if [ $(id -u) != 0 ]; then\n\tosascript -e \"do shell script quoted form of \\\"${SRCROOT}/notifyd/xcodescripts/mk_notify_conf.sh\\\" with administrator privileges\"\nelse\n\texec \"${SCRIPT_INPUT_FILE_0}\"\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 1886390F20E1A22F00C8BEA9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1886391920E1A2BF00C8BEA9 /* notify_bench.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 3FA21AAC148AA8E300099D2F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6E8E6AD02282A7DA0084C085 /* notify.defs in Sources */, + 9456B8532023CAB600CF7D27 /* table.c in Sources */, 3FA21AE6148AAEAC00099D2F /* notify_ipc.defs in Sources */, 3FA21ACF148AAA5000099D2F /* notify_proc.c in Sources */, 3FA21AD0148AAA5000099D2F /* notifyd.c in Sources */, + 9456B8522023CAB300CF7D27 /* libnotify.c in Sources */, 3FA21AD1148AAA5000099D2F /* pathwatch.c in Sources */, 3FA21AD2148AAA5000099D2F /* service.c in Sources */, 3FA21AD3148AAA5000099D2F /* timer.c in Sources */, @@ -584,6 +814,18 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 94099C6F2087E7D50004B6BC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9487BF2C2089BAA30043BF74 /* table.c in Sources */, + 9487BF2A2089A99A0043BF74 /* notify_client.c in Sources */, + 94099C762087E7D50004B6BC /* RegisterTests.m in Sources */, + 9487BF2B2089A9E30043BF74 /* notify_ipc.defs in Sources */, + 9487BF2D2089BAB20043BF74 /* libnotify.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D2AAC044055464E500DB518D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -591,6 +833,7 @@ 2D312B7A102CA30200F90022 /* notify_ipc.defs in Sources */, 2D312B76102CA2E300F90022 /* libnotify.c in Sources */, 2D312B77102CA2E300F90022 /* notify_client.c in Sources */, + E6481E7F2165785F00C04412 /* notify_probes.d in Sources */, 2D312B78102CA2E300F90022 /* table.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -608,9 +851,32 @@ target = 3FA21ABD148AA8F000099D2F /* notifyutil */; targetProxy = 3FA21ACC148AA94A00099D2F /* PBXContainerItemProxy */; }; + 72FA84FF1BD6EC2000A4CC6F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 72FA84F61BD6E9DF00A4CC6F /* darwintests */; + targetProxy = 72FA84FE1BD6EC2000A4CC6F /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 1886391520E1A22F00C8BEA9 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F999964185C474E00EAD3A0 /* notifyutil.xcconfig */; + buildSettings = { + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 1886391620E1A22F00C8BEA9 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F999964185C474E00EAD3A0 /* notifyutil.xcconfig */; + buildSettings = { + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; 1DEB91ED08733DB70010E9CD /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 3F82235D12B18551005DD509 /* libnotify.xcconfig */; @@ -621,7 +887,21 @@ 1DEB91F108733DB70010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - APPLY_RULES_IN_COPY_FILES = YES; + APPLY_RULES_IN_COPY_FILES = ""; + CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LLVM_LTO = YES; + SUPPORTED_PLATFORMS = "watchsimulator watchos iphonesimulator iphoneos macosx bridgeos appletvsimulator appletvos"; + WARNING_CFLAGS = "-Wno-dollar-in-identifier-extension"; }; name = Release; }; @@ -629,6 +909,10 @@ isa = XCBuildConfiguration; baseConfigurationReference = 3F999963185C474E00EAD3A0 /* notifyd.xcconfig */; buildSettings = { + OTHER_CFLAGS = ( + "$(inherited)", + "-DSINGLE_THREADED_NOTIFY_STATE=1", + ); PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -637,6 +921,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 3F999964185C474E00EAD3A0 /* notifyutil.xcconfig */; buildSettings = { + CODE_SIGN_ENTITLEMENTS = notifyutil/notifyutil_entitlements.plist; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -649,13 +934,240 @@ }; name = Release; }; + 72FA84F81BD6E9DF00A4CC6F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = "iphoneos macosx watchos appletvos bridgeos"; + }; + name = Release; + }; + 72FA84FD1BD6EAB900A4CC6F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos"; + }; + name = Release; + }; + 94099C782087E7D50004B6BC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F82235D12B18551005DD509 /* libnotify.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = xctests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LINK_WITH_STANDARD_LIBRARIES = YES; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CFLAGS = ( + "$(inherited)", + "-DBUILDING_TESTS=1", + "-fno-lto", + ); + OTHER_MIGFLAGS = "-DBUILDING_TESTS=1"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.xctests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 949B783D208BE1F2002AD6AF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLY_RULES_IN_COPY_FILES = ""; + CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LLVM_LTO = YES; + SUPPORTED_PLATFORMS = "watchsimulator watchos iphonesimulator iphoneos macosx bridgeos appletvsimulator appletvos"; + WARNING_CFLAGS = "-Wno-dollar-in-identifier-extension"; + }; + name = Debug; + }; + 949B783E208BE1F2002AD6AF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F82235D12B18551005DD509 /* libnotify.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 949B783F208BE1F2002AD6AF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F999963185C474E00EAD3A0 /* notifyd.xcconfig */; + buildSettings = { + OTHER_CFLAGS = ( + "$(inherited)", + "-DSINGLE_THREADED_NOTIFY_STATE=1", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 949B7840208BE1F2002AD6AF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F999964185C474E00EAD3A0 /* notifyutil.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = notifyutil/notifyutil_entitlements.plist; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 949B7841208BE1F2002AD6AF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F999961185C474E00EAD3A0 /* base.xcconfig */; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 949B7842208BE1F2002AD6AF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = "iphoneos macosx watchos appletvos bridgeos"; + }; + name = Debug; + }; + 949B7843208BE1F2002AD6AF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos"; + }; + name = Debug; + }; + 949B7844208BE1F2002AD6AF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F82235D12B18551005DD509 /* libnotify.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = xctests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LINK_WITH_STANDARD_LIBRARIES = YES; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CFLAGS = ( + "$(inherited)", + "-DDEBUG=1", + "-O0", + "-DBUILDING_TESTS=1", + "-fno-lto", + ); + OTHER_MIGFLAGS = "-DBUILDING_TESTS=1"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.xctests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Debug; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 1886391420E1A22F00C8BEA9 /* Build configuration list for PBXNativeTarget "notifybench" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1886391520E1A22F00C8BEA9 /* Release */, + 1886391620E1A22F00C8BEA9 /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 1DEB91EB08733DB70010E9CD /* Build configuration list for PBXNativeTarget "libnotify" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB91ED08733DB70010E9CD /* Release */, + 949B783E208BE1F2002AD6AF /* Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -664,6 +1176,7 @@ isa = XCConfigurationList; buildConfigurations = ( 1DEB91F108733DB70010E9CD /* Release */, + 949B783D208BE1F2002AD6AF /* Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -672,6 +1185,7 @@ isa = XCConfigurationList; buildConfigurations = ( 3FA21AB9148AA8E300099D2F /* Release */, + 949B783F208BE1F2002AD6AF /* Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -680,6 +1194,7 @@ isa = XCConfigurationList; buildConfigurations = ( 3FA21AC6148AA8F000099D2F /* Release */, + 949B7840208BE1F2002AD6AF /* Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -688,6 +1203,34 @@ isa = XCConfigurationList; buildConfigurations = ( 3FA21AC9148AA93000099D2F /* Release */, + 949B7841208BE1F2002AD6AF /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 72FA84F71BD6E9DF00A4CC6F /* Build configuration list for PBXLegacyTarget "darwintests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 72FA84F81BD6E9DF00A4CC6F /* Release */, + 949B7842208BE1F2002AD6AF /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 72FA84FC1BD6EAB900A4CC6F /* Build configuration list for PBXAggregateTarget "tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 72FA84FD1BD6EAB900A4CC6F /* Release */, + 949B7843208BE1F2002AD6AF /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 94099C792087E7D50004B6BC /* Build configuration list for PBXNativeTarget "xctests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 94099C782087E7D50004B6BC /* Release */, + 949B7844208BE1F2002AD6AF /* Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/src/libnotify/Libnotify.xcodeproj/xcshareddata/xcschemes/xctests.xcscheme b/src/libnotify/Libnotify.xcodeproj/xcshareddata/xcschemes/xctests.xcscheme new file mode 100644 index 000000000..3bdcd8937 --- /dev/null +++ b/src/libnotify/Libnotify.xcodeproj/xcshareddata/xcschemes/xctests.xcscheme @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libnotify/include/libnotify.h b/src/libnotify/include/libnotify.h deleted file mode 120000 index 73059b9ef..000000000 --- a/src/libnotify/include/libnotify.h +++ /dev/null @@ -1 +0,0 @@ -../libnotify.h \ No newline at end of file diff --git a/src/libnotify/include/notify.h b/src/libnotify/include/notify.h deleted file mode 120000 index 39734f9fd..000000000 --- a/src/libnotify/include/notify.h +++ /dev/null @@ -1 +0,0 @@ -../notify.h \ No newline at end of file diff --git a/src/libnotify/include/notify_internal.h b/src/libnotify/include/notify_internal.h deleted file mode 120000 index a83846e63..000000000 --- a/src/libnotify/include/notify_internal.h +++ /dev/null @@ -1 +0,0 @@ -../notify_internal.h \ No newline at end of file diff --git a/src/libnotify/include/notify_ipc_types.h b/src/libnotify/include/notify_ipc_types.h deleted file mode 120000 index 7d46249c7..000000000 --- a/src/libnotify/include/notify_ipc_types.h +++ /dev/null @@ -1 +0,0 @@ -../notify_ipc_types.h \ No newline at end of file diff --git a/src/libnotify/include/notify_keys.h b/src/libnotify/include/notify_keys.h deleted file mode 120000 index 033764423..000000000 --- a/src/libnotify/include/notify_keys.h +++ /dev/null @@ -1 +0,0 @@ -../notify_keys.h \ No newline at end of file diff --git a/src/libnotify/include/notify_private.h b/src/libnotify/include/notify_private.h deleted file mode 120000 index ebf5bb281..000000000 --- a/src/libnotify/include/notify_private.h +++ /dev/null @@ -1 +0,0 @@ -../notify_private.h \ No newline at end of file diff --git a/src/libnotify/include/table.h b/src/libnotify/include/table.h deleted file mode 120000 index e203a49c2..000000000 --- a/src/libnotify/include/table.h +++ /dev/null @@ -1 +0,0 @@ -../table.h \ No newline at end of file diff --git a/src/libnotify/libnotify.c b/src/libnotify/libnotify.c index 1bfe45356..fa1e618ca 100644 --- a/src/libnotify/libnotify.c +++ b/src/libnotify/libnotify.c @@ -21,6 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include #include #include #include @@ -34,159 +35,169 @@ #include #include #include -#include +#include +#include +#include #include "libnotify.h" #include "notify.h" #include "notify_internal.h" -#define USER_PROTECTED_UID_PREFIX "user.uid." -#define USER_PROTECTED_UID_PREFIX_LEN 9 -uint64_t -make_client_id(pid_t pid, int token) +#pragma mark - +const char * +_notify_shm_id(void) { - uint64_t cid; - - cid = pid; - cid <<= 32; - cid |= token; - - return cid; +#if TARGET_OS_SIMULATOR + static dispatch_once_t once; + static char *shm_id; + + dispatch_once(&once, ^{ + /* + * According to documentation, our shm_id must be no more than 31 characters long + * but in practice, even 31 characters is too long (), + * so we jump through some hoops to make a smaller string based on our UDID. + */ + const char *udid = getenv("SIMULATOR_UDID"); + if (udid && strlen(udid) == 36) { + char scratch[34]; // 32 characters, 2 NUL + + /* 01234567890123456789012345678901234567890 */ + /* UUUUUUUU-UUUU-UUUU-LLLL-LLLLLLLLLLLL */ + memcpy(scratch, udid, 8); + memcpy(scratch+8, udid+9, 4); + memcpy(scratch+12, udid+14, 4); + scratch[16] = '\0'; + + memcpy(scratch+17, udid+19, 4); + memcpy(scratch+21, udid+24, 12); + scratch[33] = '\0'; + + /* + * If the input is invalid, these will end up being undefined + * values, but they'll still be values we can use. + */ + uint64_t upper = strtoull(scratch, NULL, 16); + uint64_t lower = strtoull(scratch + 17, NULL, 16); + + const char *c64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + scratch[0] = c64[(upper >> 57) & 0xf]; + scratch[1] = c64[(upper >> 50) & 0xf]; + scratch[2] = c64[(upper >> 43) & 0xf]; + scratch[3] = c64[(upper >> 36) & 0xf]; + scratch[4] = c64[(upper >> 29) & 0xf]; + scratch[5] = c64[(upper >> 22) & 0xf]; + scratch[6] = c64[(upper >> 15) & 0xf]; + scratch[7] = c64[(upper >> 8) & 0xf]; + scratch[8] = c64[(upper >> 1) & 0xf]; + /* Drop a bit on the floor, but that probably doesn't matter. It does not need to be reversible */ + + scratch[10] = c64[(lower >> 57) & 0xf]; + scratch[11] = c64[(lower >> 50) & 0xf]; + scratch[12] = c64[(lower >> 43) & 0xf]; + scratch[13] = c64[(lower >> 36) & 0xf]; + scratch[14] = c64[(lower >> 29) & 0xf]; + scratch[15] = c64[(lower >> 22) & 0xf]; + scratch[16] = c64[(lower >> 15) & 0xf]; + scratch[17] = c64[(lower >> 8) & 0xf]; + scratch[18] = c64[(lower >> 1) & 0xf]; + /* Drop a bit on the floor, but that probably doesn't matter. It does not need to be reversible */ + + scratch[19] = '\0'; + + asprintf(&shm_id, "sim.not.%s", scratch); + assert(shm_id); + } + + if (shm_id == NULL) + { + shm_id = "apple.shm.notification_center"; + } + }); + + return shm_id; +#else + return "apple.shm.notification_center"; +#endif } -notify_state_t * -_notify_lib_notify_state_new(uint32_t flags, uint32_t table_size) +inline uint64_t +make_client_id(pid_t pid, int token) { - notify_state_t *ns; - - ns = (notify_state_t *)calloc(1, sizeof(notify_state_t)); - if (ns == NULL) return NULL; - - ns->flags = flags; - ns->sock = -1; - - if (ns->flags & NOTIFY_STATE_USE_LOCKS) - { - ns->lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); - if (ns->lock == NULL) - { - free(ns); - return NULL; - } - - pthread_mutex_init(ns->lock, NULL); - } - - ns->name_table = _nc_table_new(table_size); - ns->name_id_table = _nc_table_new(table_size); - ns->client_table = _nc_table_new(table_size); - ns->port_table = _nc_table_new(table_size); - ns->proc_table = _nc_table_new(table_size); - - if ((ns->name_table == NULL) || (ns->name_id_table == NULL) || (ns->client_table == NULL) || (ns->port_table == NULL) || (ns->proc_table == NULL)) - { - free(ns->lock); - _nc_table_free(ns->name_table); - _nc_table_free(ns->name_id_table); - _nc_table_free(ns->client_table); - _nc_table_free(ns->port_table); - _nc_table_free(ns->proc_table); - free(ns); - return NULL; - } - - return ns; + union client_id cid = { + .pid = pid, + .token = token, + }; + return cid.hash_key; } void -_notify_lib_notify_state_free(notify_state_t *ns) +_notify_lib_notify_state_init(notify_state_t * ns, uint32_t flags) { - if (ns == NULL) return; +#ifdef SINGLE_THREADED_NOTIFY_STATE + assert((flags & NOTIFY_STATE_USE_LOCKS) == 0); +#endif + ns->flags = flags; + ns->sock = -1; + ns->lock = OS_UNFAIR_LOCK_INIT; + _nc_table_init(&ns->name_table, offsetof(name_info_t, name)); + _nc_table_init_64(&ns->name_id_table, offsetof(name_info_t, name_id)); + _nc_table_init_64(&ns->client_table, offsetof(client_t, cid.hash_key)); + _nc_table_init_n(&ns->port_table, offsetof(port_data_t, port)); + _nc_table_init_n(&ns->proc_table, offsetof(proc_data_t, pid)); +} - _nc_table_free(ns->name_table); - _nc_table_free(ns->name_id_table); - _nc_table_free(ns->client_table); - _nc_table_free(ns->port_table); - _nc_table_free(ns->proc_table); +// We only need to lock in the client +inline static void +_notify_state_lock(os_unfair_lock_t lock) +{ +#ifndef SINGLE_THREADED_NOTIFY_STATE + os_unfair_lock_lock_with_options(lock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); +#endif +} - if (ns->lock != NULL) - { - pthread_mutex_destroy(ns->lock); - free(ns->lock); - } - - if (ns->sock != -1) - { - shutdown(ns->sock, 2); - close(ns->sock); - } - - if (ns->controlled_name != NULL) free(ns->controlled_name); +inline static void +_notify_state_unlock(os_unfair_lock_t lock) +{ +#ifndef SINGLE_THREADED_NOTIFY_STATE + os_unfair_lock_unlock(lock); +#endif } static client_t * -_internal_client_new(notify_state_t *ns, pid_t pid, int token) +_internal_client_new(notify_state_t *ns, pid_t pid, int token, name_info_t *n) { client_t *c; uint64_t cid = make_client_id(pid, token); - if (ns == NULL) return NULL; - /* detect duplicates - should never happen, but it would be bad */ - c = _nc_table_find_64(ns->client_table, cid); + c = _nc_table_find_64(&ns->client_table, cid); if (c != NULL) return NULL; c = calloc(1, sizeof(client_t)); if (c == NULL) return NULL; ns->stat_client_alloc++; + c->cid.hash_key = cid; + c->name_info = n; - c->client_id = cid; - c->pid = pid; - c->send_val = token; - - _nc_table_insert_64(ns->client_table, cid, c); + LIST_INSERT_HEAD(&n->subscriptions, c, client_subscription_entry); + n->refcount++; + _nc_table_insert_64(&ns->client_table, &c->cid.hash_key); return c; } static void _internal_client_release(notify_state_t *ns, client_t *c) { - uint64_t cid; + _nc_table_delete_64(&ns->client_table, c->cid.hash_key); - if (ns == NULL) return; - if (c == NULL) return; - - cid = c->client_id; - _nc_table_delete_64(ns->client_table, cid); - - switch (c->notify_type) - { - case NOTIFY_TYPE_SIGNAL: - { - break; - } - case NOTIFY_TYPE_FILE: - { - if (c->fd > 0) close(c->fd); - c->fd = -1; - break; - } - case NOTIFY_TYPE_PORT: - { - if (c->port != MACH_PORT_NULL) - { - /* release my send right to the port */ - mach_port_deallocate(mach_task_self(), c->port); - } - break; - } - default: - { - break; - } + if (c->state_and_type & NOTIFY_TYPE_FILE) { + if (c->deliver.fd >= 0) close(c->deliver.fd); + } else if (c->state_and_type & NOTIFY_TYPE_PORT) { + /* release my send right to the port */ + mach_port_deallocate(mach_task_self(), c->deliver.port); } free(c); @@ -199,7 +210,6 @@ _internal_new_name(notify_state_t *ns, const char *name) name_info_t *n; size_t namelen; - if (ns == NULL) return NULL; if (name == NULL) return NULL; namelen = strlen(name) + 1; @@ -212,15 +222,15 @@ _internal_new_name(notify_state_t *ns, const char *name) n->name = (char *)n + sizeof(name_info_t); memcpy(n->name, name, namelen); - notify_globals_t globals = _notify_globals(); - n->name_id = globals->name_id++; - + n->name_id = ns->name_id++; n->access = NOTIFY_ACCESS_DEFAULT; n->slot = (uint32_t)-1; n->val = 1; - _nc_table_insert_no_copy(ns->name_table, n->name, n); - _nc_table_insert_64(ns->name_id_table, n->name_id, n); + LIST_INIT(&n->subscriptions); + + _nc_table_insert(&ns->name_table, &n->name); + _nc_table_insert_64(&ns->name_id_table, &n->name_id); return n; } @@ -230,7 +240,6 @@ _internal_insert_controlled_name(notify_state_t *ns, name_info_t *n) { int i, j; - if (ns == NULL) return; if (n == NULL) return; if (ns->controlled_name == NULL) ns->controlled_name_count = 0; @@ -295,12 +304,12 @@ _internal_remove_controlled_name(notify_state_t *ns, name_info_t *n) static uint32_t _internal_check_access(notify_state_t *ns, const char *name, uid_t uid, gid_t gid, int req) { - uint32_t i, len, plen; + uint32_t i; + size_t len, plen; name_info_t *p; char str[64]; - if (ns == NULL) return NOTIFY_STATUS_FAILED; - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; + if (name == NULL) return NOTIFY_STATUS_NULL_INPUT; /* root may do anything */ if (uid == 0) return NOTIFY_STATUS_OK; @@ -345,134 +354,55 @@ _notify_lib_check_controlled_access(notify_state_t *ns, char *name, uid_t uid, g { uint32_t status; - if (ns == NULL) return NOTIFY_STATUS_FAILED; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); + _notify_state_lock(&ns->lock); status = _internal_check_access(ns, name, uid, gid, req); - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return status; } -uint32_t -_notify_lib_port_proc_new(notify_state_t *ns, mach_port_t port, pid_t proc, uint32_t state, dispatch_source_t src) -{ - portproc_data_t *pdata; - - if (ns == NULL) return NOTIFY_STATUS_FAILED; - if ((proc == 0) && (port == MACH_PORT_NULL)) return NOTIFY_STATUS_FAILED; - - pdata = (portproc_data_t *)calloc(1, sizeof(portproc_data_t)); - if (pdata == NULL) return NOTIFY_STATUS_FAILED; - - ns->stat_portproc_alloc++; - - pdata->refcount = 1; - pdata->flags = state; - pdata->src = src; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - if (proc == 0) _nc_table_insert_n(ns->port_table, port, pdata); - else _nc_table_insert_n(ns->proc_table, proc, pdata); - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - - return NOTIFY_STATUS_OK; -} - -portproc_data_t * -_notify_lib_port_proc_find(notify_state_t *ns, mach_port_t port, pid_t proc) -{ - portproc_data_t *pdata = NULL; - - if (ns == NULL) return NULL; - if ((proc == 0) && (port == MACH_PORT_NULL)) return NULL; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - if (proc == 0) pdata = _nc_table_find_n(ns->port_table, port); - else pdata = _nc_table_find_n(ns->proc_table, proc); - - if (pdata != NULL) pdata->refcount++; - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - - return pdata; -} - -void -_notify_lib_port_proc_release(notify_state_t *ns, mach_port_t port, pid_t proc) -{ - portproc_data_t *pdata = NULL; - - if (ns == NULL) return; - if ((proc == 0) && (port == MACH_PORT_NULL)) return; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - if (proc == 0) pdata = _nc_table_find_n(ns->port_table, port); - else pdata = _nc_table_find_n(ns->proc_table, proc); - - if (pdata != NULL) - { - if (pdata->refcount > 0) pdata->refcount--; - if (pdata->refcount == 0) - { - if (proc == 0) _nc_table_delete_n(ns->port_table, port); - else _nc_table_delete_n(ns->proc_table, proc); - - dispatch_source_cancel(pdata->src); - dispatch_release(pdata->src); - - free(pdata); - ns->stat_portproc_free++; - } - } - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); -} - /* * Send notification to a subscriber */ static uint32_t -_internal_send(notify_state_t *ns, client_t *c) +_internal_send(notify_state_t *ns, client_t *c, + proc_data_t *proc_data, port_data_t *port_data) { uint32_t send; - portproc_data_t *pdata; - if (ns == NULL) return NOTIFY_STATUS_FAILED; - if (c == NULL) return NOTIFY_STATUS_FAILED; - - if (c->state & NOTIFY_CLIENT_STATE_SUSPENDED) + if (c->state_and_type & NOTIFY_CLIENT_STATE_SUSPENDED) { - c->state |= NOTIFY_CLIENT_STATE_PENDING; + c->state_and_type |= NOTIFY_CLIENT_STATE_PENDING; return NOTIFY_STATUS_OK; } - pdata = _nc_table_find_n(ns->proc_table, c->pid); - if ((pdata != NULL) && (pdata->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED)) + if (proc_data == NULL) + { + proc_data = _nc_table_find_n(&ns->proc_table, c->cid.pid); + } + if ((proc_data != NULL) && (proc_data->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED)) { c->suspend_count++; - c->state |= NOTIFY_CLIENT_STATE_SUSPENDED; - c->state |= NOTIFY_CLIENT_STATE_PENDING; + c->state_and_type |= NOTIFY_CLIENT_STATE_SUSPENDED; + c->state_and_type |= NOTIFY_CLIENT_STATE_PENDING; return NOTIFY_STATUS_OK; } - send = c->send_val; + send = c->cid.token; - switch (c->notify_type) + switch (c->state_and_type & NOTIFY_TYPE_MASK) { case NOTIFY_TYPE_SIGNAL: { int rc = 0; - if (c->pid == NOTIFY_CLIENT_SELF) rc = kill(getpid(), c->sig); - else rc = kill(c->pid, c->sig); + if (c->cid.pid == NOTIFY_CLIENT_SELF) rc = kill(getpid(), c->deliver.sig); + else rc = kill(c->cid.pid, c->deliver.sig); - if (rc != 0) return NOTIFY_STATUS_FAILED; + if (rc != 0) return NOTIFY_STATUS_KILL_FAILED; - c->state &= ~NOTIFY_CLIENT_STATE_PENDING; - c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; + c->state_and_type &= ~NOTIFY_CLIENT_STATE_PENDING; + c->state_and_type &= ~NOTIFY_CLIENT_STATE_TIMEOUT; return NOTIFY_STATUS_OK; } @@ -481,20 +411,20 @@ _internal_send(notify_state_t *ns, client_t *c) { ssize_t len; - if (c->fd >= 0) + if (c->deliver.fd >= 0) { send = htonl(send); - len = write(c->fd, &send, sizeof(uint32_t)); + len = write(c->deliver.fd, &send, sizeof(uint32_t)); if (len != sizeof(uint32_t)) { - close(c->fd); - c->fd = -1; - return NOTIFY_STATUS_FAILED; + close(c->deliver.fd); + c->deliver.fd = -1; + return NOTIFY_STATUS_WRITE_FAILED; } } - c->state &= ~NOTIFY_CLIENT_STATE_PENDING; - c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; + c->state_and_type &= ~NOTIFY_CLIENT_STATE_PENDING; + c->state_and_type &= ~NOTIFY_CLIENT_STATE_TIMEOUT; return NOTIFY_STATUS_OK; } @@ -505,12 +435,15 @@ _internal_send(notify_state_t *ns, client_t *c) mach_msg_empty_send_t msg; mach_msg_option_t opts = MACH_SEND_MSG | MACH_SEND_TIMEOUT; - pdata = _nc_table_find_n(ns->port_table, c->port); - if ((pdata != NULL) && (pdata->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED)) + if (port_data == NULL) + { + port_data = _nc_table_find_n(&ns->port_table, c->deliver.port); + } + if ((port_data != NULL) && (port_data->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED)) { c->suspend_count++; - c->state |= NOTIFY_CLIENT_STATE_SUSPENDED; - c->state |= NOTIFY_CLIENT_STATE_PENDING; + c->state_and_type |= NOTIFY_CLIENT_STATE_SUSPENDED; + c->state_and_type |= NOTIFY_CLIENT_STATE_PENDING; return NOTIFY_STATUS_OK; } @@ -520,7 +453,7 @@ _internal_send(notify_state_t *ns, client_t *c) msg.header.msgh_size = sizeof(mach_msg_empty_send_t); msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO); msg.header.msgh_local_port = MACH_PORT_NULL; - msg.header.msgh_remote_port = c->port; + msg.header.msgh_remote_port = c->deliver.port; msg.header.msgh_id = (mach_msg_id_t)send; kstatus = mach_msg(&msg.header, opts, msg.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); @@ -537,19 +470,26 @@ _internal_send(notify_state_t *ns, client_t *c) * c->suspend_count must be zero, or we would not be trying to send. */ c->suspend_count++; - c->state |= NOTIFY_CLIENT_STATE_SUSPENDED; - c->state |= NOTIFY_CLIENT_STATE_PENDING; - c->state |= NOTIFY_CLIENT_STATE_TIMEOUT; + c->state_and_type |= NOTIFY_CLIENT_STATE_SUSPENDED; + c->state_and_type |= NOTIFY_CLIENT_STATE_PENDING; + c->state_and_type |= NOTIFY_CLIENT_STATE_TIMEOUT; + if (port_data) { + /* + * If we failed to send, stop trying on this port + * and just wait for the send-possible notification + */ + port_data->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED; + } return NOTIFY_STATUS_OK; } - return NOTIFY_STATUS_FAILED; + return NOTIFY_STATUS_MACH_MSG_TIMEOUT; } - else if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; + else if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_MACH_MSG_FAILED; - c->state &= ~NOTIFY_CLIENT_STATE_PENDING; - c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; + c->state_and_type &= ~NOTIFY_CLIENT_STATE_PENDING; + c->state_and_type &= ~NOTIFY_CLIENT_STATE_TIMEOUT; return NOTIFY_STATUS_OK; } @@ -560,8 +500,8 @@ _internal_send(notify_state_t *ns, client_t *c) } } - c->state &= ~NOTIFY_CLIENT_STATE_PENDING; - c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; + c->state_and_type &= ~NOTIFY_CLIENT_STATE_PENDING; + c->state_and_type &= ~NOTIFY_CLIENT_STATE_TIMEOUT; return NOTIFY_STATUS_OK; } @@ -571,12 +511,11 @@ _notify_lib_post_client(notify_state_t *ns, client_t *c) { uint32_t status; - if (ns == NULL) return NOTIFY_STATUS_FAILED; - if (c == NULL) return NOTIFY_STATUS_FAILED; + if (c == NULL) return NOTIFY_STATUS_NULL_INPUT; - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - status = _internal_send(ns, c); - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_lock(&ns->lock); + status = _internal_send(ns, c, NULL, NULL); + _notify_state_unlock(&ns->lock); return status; } @@ -585,7 +524,6 @@ static uint32_t _internal_post_name(notify_state_t *ns, name_info_t *n, uid_t uid, gid_t gid) { int auth; - list_t *l; client_t *c; if (n == NULL) return NOTIFY_STATUS_INVALID_NAME; @@ -595,10 +533,8 @@ _internal_post_name(notify_state_t *ns, name_info_t *n, uid_t uid, gid_t gid) n->val++; - for (l = n->subscriptions; l != NULL; l = _nc_list_next(l)) - { - c = _nc_list_data(l); - if (c != NULL) _internal_send(ns, c); + LIST_FOREACH(c, &n->subscriptions, client_subscription_entry) { + _internal_send(ns, c, NULL, NULL); } return NOTIFY_STATUS_OK; @@ -613,20 +549,18 @@ _notify_lib_post(notify_state_t *ns, const char *name, uid_t uid, gid_t gid) name_info_t *n; uint32_t status; - if (ns == NULL) return NOTIFY_STATUS_FAILED; + _notify_state_lock(&ns->lock); - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - n = (name_info_t *)_nc_table_find(ns->name_table, name); + n = _nc_table_find(&ns->name_table, name); if (n == NULL) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_INVALID_NAME; } status = _internal_post_name(ns, n, uid, gid); - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return status; } @@ -636,36 +570,32 @@ _notify_lib_post_nid(notify_state_t *ns, uint64_t nid, uid_t uid, gid_t gid) name_info_t *n; uint32_t status; - if (ns == NULL) return NOTIFY_STATUS_FAILED; + _notify_state_lock(&ns->lock); - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid); + n = _nc_table_find_64(&ns->name_id_table, nid); if (n == NULL) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_INVALID_NAME; } status = _internal_post_name(ns, n, uid, gid); - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return status; } static void _internal_release_name_info(notify_state_t *ns, name_info_t *n) { - if (ns == NULL) return; if (n == NULL) return; if (n->refcount > 0) n->refcount--; if (n->refcount == 0) { _internal_remove_controlled_name(ns, n); - _nc_table_delete(ns->name_table, n->name); - _nc_table_delete_64(ns->name_id_table, n->name_id); - _nc_list_release_list(n->subscriptions); + _nc_table_delete(&ns->name_table, n->name); + _nc_table_delete_64(&ns->name_id_table, n->name_id); free(n); ns->stat_name_free++; } @@ -675,285 +605,104 @@ _internal_release_name_info(notify_state_t *ns, name_info_t *n) * Cancel (delete) a client */ static void -_internal_cancel(notify_state_t *ns, uint64_t cid) +_internal_cancel(notify_state_t *ns, client_t *c) { - client_t *c; - name_info_t *n; + name_info_t *n = c->name_info; - if (ns == NULL) return; - - c = NULL; - n = NULL; - - c = _nc_table_find_64(ns->client_table, cid); - if (c == NULL) return; - - n = c->name_info; - if (n == NULL) return; - - n->subscriptions =_nc_list_find_release(n->subscriptions, c); + LIST_REMOVE(c, client_subscription_entry); _internal_client_release(ns, c); _internal_release_name_info(ns, n); } void -_notify_lib_cancel(notify_state_t *ns, pid_t pid, int token) +_notify_lib_cancel_client(notify_state_t *ns, client_t *c) { - uint64_t cid; - - if (ns == NULL) return; - - cid = make_client_id(pid, token); - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - _internal_cancel(ns, cid); - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_lock(&ns->lock); + _internal_cancel(ns, c); + _notify_state_unlock(&ns->lock); } void +_notify_lib_cancel(notify_state_t *ns, pid_t pid, int token) +{ + uint64_t cid = make_client_id(pid, token); + client_t *c; + + _notify_state_lock(&ns->lock); + c = _nc_table_find_64(&ns->client_table, cid); + if (c) { + _internal_cancel(ns, c); + } + _notify_state_unlock(&ns->lock); +} + +uint32_t _notify_lib_suspend(notify_state_t *ns, pid_t pid, int token) { + uint64_t cid = make_client_id(pid, token); + uint32_t result = NOTIFY_STATUS_OK; client_t *c; - uint64_t cid; - if (ns == NULL) return; + _notify_state_lock(&ns->lock); - cid = make_client_id(pid, token); - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - c = _nc_table_find_64(ns->client_table, cid); - if (c != NULL) - { - c->state |= NOTIFY_CLIENT_STATE_SUSPENDED; - if (c->suspend_count < UINT32_MAX) c->suspend_count++; + c = _nc_table_find_64(&ns->client_table, cid); + if (c != NULL) { + c->state_and_type |= NOTIFY_CLIENT_STATE_SUSPENDED; + if (c->suspend_count < UINT8_MAX) c->suspend_count++; + } else { + result = NOTIFY_STATUS_CLIENT_NOT_FOUND; } - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); + + return result; +} + +static void +_internal_resume(notify_state_t *ns, client_t *c, + proc_data_t *proc_data, port_data_t *port_data) +{ + if (c->suspend_count > 0) c->suspend_count--; + if (c->suspend_count == 0) { + c->state_and_type &= ~NOTIFY_CLIENT_STATE_SUSPENDED; + c->state_and_type &= ~NOTIFY_CLIENT_STATE_TIMEOUT; + + if (c->state_and_type & NOTIFY_CLIENT_STATE_PENDING) { + _internal_send(ns, c, proc_data, port_data); + } + } +} + +void +_notify_lib_resume_client(notify_state_t *ns, client_t *c, + proc_data_t *proc_data, port_data_t *port_data) +{ + _notify_state_lock(&ns->lock); + _internal_resume(ns, c, proc_data, port_data); + _notify_state_unlock(&ns->lock); } uint32_t _notify_lib_resume(notify_state_t *ns, pid_t pid, int token) { - client_t *c; - uint64_t cid; uint32_t status = NOTIFY_STATUS_OK; + uint64_t cid = make_client_id(pid, token); + client_t *c; - if (ns == NULL) return NOTIFY_STATUS_FAILED; + _notify_state_lock(&ns->lock); - cid = make_client_id(pid, token); - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - c = _nc_table_find_64(ns->client_table, cid); - if (c != NULL) - { - if (c->suspend_count > 0) c->suspend_count--; - if (c->suspend_count == 0) - { - c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED; - c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; - - if (c->state & NOTIFY_CLIENT_STATE_PENDING) - { - status = _internal_send(ns, c); - } - } + c = _nc_table_find_64(&ns->client_table, cid); + if (c != NULL) { + _internal_resume(ns, c, NULL, NULL); + } else { + status = NOTIFY_STATUS_CLIENT_NOT_FOUND; } - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return status; } -void -_notify_lib_suspend_proc(notify_state_t *ns, pid_t pid) -{ - portproc_data_t *pdata; - - if (ns == NULL) return; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - pdata = _nc_table_find_n(ns->proc_table, pid); - if (pdata != NULL) pdata->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED; - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); -} - -void -_notify_lib_resume_proc(notify_state_t *ns, pid_t pid) -{ - client_t *c; - void *tt; - portproc_data_t *pdata; - - if (ns == NULL) return; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - /* Resume all subscriptions for this process */ - pdata = _nc_table_find_n(ns->proc_table, pid); - if (pdata != NULL) pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED; - - tt = _nc_table_traverse_start(ns->client_table); - while (tt != NULL) - { - c = _nc_table_traverse(ns->client_table, tt); - if (c == NULL) break; - - if (c->pid == pid) - { - if (c->suspend_count > 0) c->suspend_count--; - if (c->suspend_count == 0) - { - c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED; - c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; - - if (c->state & NOTIFY_CLIENT_STATE_PENDING) - { - _internal_send(ns, c); - } - } - } - } - _nc_table_traverse_end(ns->client_table, tt); - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); -} - -void -_notify_lib_suspend_port(notify_state_t *ns, mach_port_t port) -{ - portproc_data_t *pdata; - - if (ns == NULL) return; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - pdata = _nc_table_find_n(ns->port_table, port); - if (pdata != NULL) pdata->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED; - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); -} - -void -_notify_lib_resume_port(notify_state_t *ns, mach_port_t port) -{ - client_t *c; - void *tt; - portproc_data_t *pdata; - - if (ns == NULL) return; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - /* Resume all subscriptions with this port */ - pdata = _nc_table_find_n(ns->port_table, port); - if (pdata != NULL) pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED; - - tt = _nc_table_traverse_start(ns->client_table); - while (tt != NULL) - { - c = _nc_table_traverse(ns->client_table, tt); - if (c == NULL) break; - - if (c->port == port) - { - if (c->suspend_count > 0) c->suspend_count--; - if (c->suspend_count == 0) - { - c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED; - c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; - - if (c->state & NOTIFY_CLIENT_STATE_PENDING) - { - _internal_send(ns, c); - } - } - } - } - _nc_table_traverse_end(ns->client_table, tt); - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); -} - -/* - * Delete all clients for a process - * N.B. notifyd does not use this routine. - */ -void -_notify_lib_cancel_proc(notify_state_t *ns, pid_t pid) -{ - client_t *c; - void *tt; - list_t *l, *x; - - if (ns == NULL) return; - - x = NULL; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - tt = _nc_table_traverse_start(ns->client_table); - while (tt != NULL) - { - c = _nc_table_traverse(ns->client_table, tt); - if (c == NULL) break; - - if (c->pid == pid) x = _nc_list_prepend(x, _nc_list_new(c)); - } - _nc_table_traverse_end(ns->client_table, tt); - - for (l = x; l != NULL; l = _nc_list_next(l)) - { - c = _nc_list_data(l); - _internal_cancel(ns, c->client_id); - } - - _nc_list_release_list(x); - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); -} - -/* - * Delete all clients for a port - * N.B. notifyd does not use this routine. - */ -void -_notify_lib_cancel_port(notify_state_t *ns, mach_port_t port) -{ - client_t *c; - void *tt; - list_t *l, *x; - - if (ns == NULL) return; - - x = NULL; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - tt = _nc_table_traverse_start(ns->client_table); - while (tt != NULL) - { - c = _nc_table_traverse(ns->client_table, tt); - if (c == NULL) break; - - if (c->port == port) x = _nc_list_prepend(x, _nc_list_new(c)); - } - _nc_table_traverse_end(ns->client_table, tt); - - for (l = x; l != NULL; l = _nc_list_next(l)) - { - c = _nc_list_data(l); - _internal_cancel(ns, c->client_id); - } - - _nc_list_release_list(x); - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); -} - /* * Check if a name has changed since the last time this client checked. * Returns true, false, or error. @@ -964,108 +713,61 @@ _notify_lib_check(notify_state_t *ns, pid_t pid, int token, int *check) client_t *c; uint64_t cid; - if (ns == NULL) return NOTIFY_STATUS_FAILED; - if (check == NULL) return NOTIFY_STATUS_FAILED; + if (check == NULL) return NOTIFY_STATUS_NULL_INPUT; cid = make_client_id(pid, token); - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); + _notify_state_lock(&ns->lock); - c = _nc_table_find_64(ns->client_table, cid); + c = _nc_table_find_64(&ns->client_table, cid); if (c == NULL) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_INVALID_TOKEN; - } - - if (c->name_info == NULL) - { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_INVALID_TOKEN; } if (c->name_info->val == c->lastval) { *check = 0; - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_OK; } c->lastval = c->name_info->val; *check = 1; - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_OK; } /* * SPI: get value for a name. */ -uint32_t +__private_extern__ uint32_t _notify_lib_peek(notify_state_t *ns, pid_t pid, int token, int *val) { - client_t *c; - uint64_t cid; + client_t *c; + uint64_t cid; - if (ns == NULL) return NOTIFY_STATUS_FAILED; - if (val == NULL) return NOTIFY_STATUS_FAILED; - - cid = make_client_id(pid, token); - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - c = _nc_table_find_64(ns->client_table, cid); - - if (c == NULL) - { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_INVALID_TOKEN; - } - - if (c->name_info == NULL) - { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_INVALID_TOKEN; - } - - *val = c->name_info->val; - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_OK; -} - -int * -_notify_lib_check_addr(notify_state_t *ns, pid_t pid, int token) -{ - client_t *c; - int *addr; - uint64_t cid; - - if (ns == NULL) return NULL; - - cid = make_client_id(pid, token); - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - c = _nc_table_find_64(ns->client_table, cid); - - if (c == NULL) - { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NULL; - } - - if (c->name_info == NULL) - { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NULL; - } - - addr = (int *)&(c->name_info->val); - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return addr; + if (val == NULL) return NOTIFY_STATUS_NULL_INPUT; + + cid = make_client_id(pid, token); + + _notify_state_lock(&ns->lock); + + c = _nc_table_find_64(&ns->client_table, cid); + + if (c == NULL) + { + _notify_state_unlock(&ns->lock); + return NOTIFY_STATUS_INVALID_TOKEN; + } + + *val = c->name_info->val; + + _notify_state_unlock(&ns->lock); + return NOTIFY_STATUS_OK; } /* @@ -1076,18 +778,17 @@ _notify_lib_get_state(notify_state_t *ns, uint64_t nid, uint64_t *state, uid_t u { name_info_t *n; - if (ns == NULL) return NOTIFY_STATUS_FAILED; - if (state == NULL) return NOTIFY_STATUS_FAILED; + if (state == NULL) return NOTIFY_STATUS_NULL_INPUT; *state = 0; - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); + _notify_state_lock(&ns->lock); - n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid); + n = _nc_table_find_64(&ns->name_id_table, nid); if (n == NULL) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_INVALID_NAME; } @@ -1095,14 +796,14 @@ _notify_lib_get_state(notify_state_t *ns, uint64_t nid, uint64_t *state, uid_t u int auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_READ); if (auth != 0) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_NOT_AUTHORIZED; } #endif *state = n->state; - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_OK; } @@ -1115,29 +816,27 @@ _notify_lib_set_state(notify_state_t *ns, uint64_t nid, uint64_t state, uid_t ui name_info_t *n; int auth; - if (ns == NULL) return NOTIFY_STATUS_FAILED; + _notify_state_lock(&ns->lock); - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid); + n = _nc_table_find_64(&ns->name_id_table, nid); if (n == NULL) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_INVALID_NAME; } auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_WRITE); if (auth != 0) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_NOT_AUTHORIZED; } n->state = state; n->state_time = mach_absolute_time(); - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_OK; } @@ -1149,7 +848,6 @@ _internal_register_common(notify_state_t *ns, const char *name, pid_t pid, int t int is_new_name; uint32_t status; - if (ns == NULL) return NOTIFY_STATUS_FAILED; if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; if (outc == NULL) return NOTIFY_STATUS_OK; @@ -1159,34 +857,28 @@ _internal_register_common(notify_state_t *ns, const char *name, pid_t pid, int t *outc = NULL; is_new_name = 0; - n = (name_info_t *)_nc_table_find(ns->name_table, name); + n = _nc_table_find(&ns->name_table, name); if (n == NULL) { is_new_name = 1; n = _internal_new_name(ns, name); - if (n == NULL) return NOTIFY_STATUS_FAILED; + if (n == NULL) return NOTIFY_STATUS_NEW_NAME_FAILED; } - c = _internal_client_new(ns, pid, token); + c = _internal_client_new(ns, pid, token, n); if (c == NULL) { if (is_new_name == 1) { - _nc_table_delete(ns->name_table, n->name); - _nc_list_release_list(n->subscriptions); + _nc_table_delete(&ns->name_table, n->name); free(n); ns->stat_name_free++; } - return NOTIFY_STATUS_FAILED; + return NOTIFY_STATUS_NEW_CLIENT_FAILED; } - n->refcount++; - - c->name_info = n; - n->subscriptions = _nc_list_prepend(n->subscriptions, _nc_list_new(c)); - *outc = c; return NOTIFY_STATUS_OK; @@ -1202,26 +894,26 @@ _notify_lib_register_signal(notify_state_t *ns, const char *name, pid_t pid, int client_t *c; uint32_t status; - if (ns == NULL) return NOTIFY_STATUS_FAILED; if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; c = NULL; - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); + _notify_state_lock(&ns->lock); status = _internal_register_common(ns, name, pid, token, uid, gid, &c); if (status != NOTIFY_STATUS_OK) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return status; } - c->notify_type = NOTIFY_TYPE_SIGNAL; - c->pid = pid; - c->sig = sig; + c->state_and_type &= ~NOTIFY_TYPE_MASK; + c->state_and_type |= NOTIFY_TYPE_SIGNAL; + c->cid.pid = pid; + c->deliver.sig = sig; *out_nid = c->name_info->name_id; - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_OK; } @@ -1235,25 +927,26 @@ _notify_lib_register_file_descriptor(notify_state_t *ns, const char *name, pid_t client_t *c; uint32_t status; - if (ns == NULL) return NOTIFY_STATUS_FAILED; if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; c = NULL; - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); + _notify_state_lock(&ns->lock); status = _internal_register_common(ns, name, pid, token, uid, gid, &c); if (status != NOTIFY_STATUS_OK) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return status; } - c->notify_type = NOTIFY_TYPE_FILE; - c->fd = fd; + c->state_and_type &= ~NOTIFY_TYPE_MASK; + c->state_and_type |= NOTIFY_TYPE_FILE; + + c->deliver.fd = fd; *out_nid = c->name_info->name_id; - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_OK; } @@ -1267,25 +960,26 @@ _notify_lib_register_mach_port(notify_state_t *ns, const char *name, pid_t pid, client_t *c; uint32_t status; - if (ns == NULL) return NOTIFY_STATUS_FAILED; if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; + if (!MACH_PORT_VALID(port)) return NOTIFY_STATUS_INVALID_PORT; c = NULL; - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); + _notify_state_lock(&ns->lock); status = _internal_register_common(ns, name, pid, token, uid, gid, &c); if (status != NOTIFY_STATUS_OK) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return status; } - c->notify_type = NOTIFY_TYPE_PORT; - c->port = port; + c->state_and_type &= ~NOTIFY_TYPE_MASK; + c->state_and_type |= NOTIFY_TYPE_PORT; + c->deliver.port = port; *out_nid = c->name_info->name_id; - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_OK; } @@ -1299,33 +993,34 @@ _notify_lib_register_plain(notify_state_t *ns, const char *name, pid_t pid, int client_t *c; uint32_t status; - if (ns == NULL) return NOTIFY_STATUS_FAILED; if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; c = NULL; - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); + _notify_state_lock(&ns->lock); status = _internal_register_common(ns, name, pid, token, uid, gid, &c); if (status != NOTIFY_STATUS_OK) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return status; } if (slot == SLOT_NONE) { - c->notify_type = NOTIFY_TYPE_PLAIN; + c->state_and_type &= ~NOTIFY_TYPE_MASK; + c->state_and_type |= NOTIFY_TYPE_PLAIN; } else { - c->notify_type = NOTIFY_TYPE_MEMORY; + c->state_and_type &= ~NOTIFY_TYPE_MASK; + c->state_and_type |= NOTIFY_TYPE_MEMORY; c->name_info->slot = slot; } *out_nid = c->name_info->name_id; - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_OK; } @@ -1334,20 +1029,19 @@ _notify_lib_set_owner(notify_state_t *ns, const char *name, uid_t uid, gid_t gid { name_info_t *n; - if (ns == NULL) return NOTIFY_STATUS_FAILED; if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); + _notify_state_lock(&ns->lock); - n = (name_info_t *)_nc_table_find(ns->name_table, name); + n = _nc_table_find(&ns->name_table, name); if (n == NULL) { /* create new name */ n = _internal_new_name(ns, name); if (n == NULL) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_FAILED; + _notify_state_unlock(&ns->lock); + return NOTIFY_STATUS_NEW_NAME_FAILED; } /* @@ -1363,62 +1057,7 @@ _notify_lib_set_owner(notify_state_t *ns, const char *name, uid_t uid, gid_t gid _internal_insert_controlled_name(ns, n); - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_OK; -} - -uint32_t -_notify_lib_get_owner(notify_state_t *ns, const char *name, uint32_t *uid, uint32_t *gid) -{ - name_info_t *n; - int i, nlen, len; - - if (ns == NULL) return NOTIFY_STATUS_FAILED; - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - n = (name_info_t *)_nc_table_find(ns->name_table, name); - if (n != NULL) - { - *uid = n->uid; - *gid = n->gid; - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_OK; - } - - len = strlen(name); - - for (i = 0; i < ns->controlled_name_count; i++) - { - n = ns->controlled_name[i]; - if (n == NULL) break; - - nlen = strlen(n->name); - - if (!strcmp(n->name, name)) - { - *uid = n->uid; - *gid = n->gid; - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_OK; - } - - /* check if this key is a prefix */ - if (nlen >= len) continue; - if (strncmp(n->name, name, nlen)) continue; - - *uid = n->uid; - *gid = n->gid; - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_OK; - } - - *uid = 0; - *gid = 0; - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_OK; } @@ -1427,20 +1066,19 @@ _notify_lib_set_access(notify_state_t *ns, const char *name, uint32_t mode) { name_info_t *n; - if (ns == NULL) return NOTIFY_STATUS_FAILED; if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); + _notify_state_lock(&ns->lock); - n = (name_info_t *)_nc_table_find(ns->name_table, name); + n = _nc_table_find(&ns->name_table, name); if (n == NULL) { /* create new name */ n = _internal_new_name(ns, name); if (n == NULL) { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_FAILED; + _notify_state_unlock(&ns->lock); + return NOTIFY_STATUS_NEW_NAME_FAILED; } /* @@ -1455,90 +1093,6 @@ _notify_lib_set_access(notify_state_t *ns, const char *name, uint32_t mode) _internal_insert_controlled_name(ns, n); - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_OK; -} - -uint32_t -_notify_lib_get_access(notify_state_t *ns, const char *name, uint32_t *mode) -{ - name_info_t *n; - int i, nlen, len; - - if (ns == NULL) return NOTIFY_STATUS_FAILED; - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - n = (name_info_t *)_nc_table_find(ns->name_table, name); - if (n != NULL) - { - *mode = n->access; - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_OK; - } - - len = strlen(name); - - for (i = 0; i < ns->controlled_name_count; i++) - { - n = ns->controlled_name[i]; - if (n == NULL) break; - if (n->name == NULL) continue; - - nlen = strlen(n->name); - - if (!strcmp(n->name, name)) - { - *mode = n->access; - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_OK; - } - - /* check if this key is a prefix */ - if (nlen >= len) continue; - if (strncmp(n->name, name, nlen)) continue; - - *mode = n->access; - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_OK; - } - - *mode = NOTIFY_ACCESS_DEFAULT; - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_OK; -} - -uint32_t -_notify_lib_release_name(notify_state_t *ns, const char *name, uid_t uid, gid_t gid) -{ - name_info_t *n; - - if (ns == NULL) return NOTIFY_STATUS_FAILED; - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - - if (ns->lock != NULL) pthread_mutex_lock(ns->lock); - - n = (name_info_t *)_nc_table_find(ns->name_table, name); - if (n == NULL) - { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_INVALID_NAME; - } - - /* Owner and root may release */ - if ((n->uid != uid) && (uid != 0)) - { - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); - return NOTIFY_STATUS_NOT_AUTHORIZED; - } - - _internal_release_name_info(ns, n); - - if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); + _notify_state_unlock(&ns->lock); return NOTIFY_STATUS_OK; } diff --git a/src/libnotify/libnotify.h b/src/libnotify/libnotify.h index e9eca4e4a..96bbd8a7e 100644 --- a/src/libnotify/libnotify.h +++ b/src/libnotify/libnotify.h @@ -24,6 +24,8 @@ #ifndef _LIBNOTIFY_H_ #define _LIBNOTIFY_H_ +#include +#include #include #include #include @@ -31,31 +33,40 @@ #include -#if TARGET_IPHONE_SIMULATOR -extern const char *_notify_shm_id(); +extern const char *_notify_shm_id(void); #define SHM_ID _notify_shm_id() -#else -#define SHM_ID "apple.shm.notification_center" -#endif #define NOTIFY_IPC_VERSION_NAME "com.apple.system.notify.ipc_version" #define NOTIFY_IPC_VERSION_NAME_LEN 35 #define NOTIFY_SERVICE_NAME "com.apple.system.notification_center" #define NOTIFY_SERVICE_NAME_LEN 36 +#define NOTIFY_IPC_VERSION_MIN_SUPPORTED 3 +#define NOTIFY_IPC_VERSION 3 -#define COMMON_PORT_KEY "com.apple.system.notify.common" +/* extra internal flags to notify_register_mach_port */ +/* Make sure this doesn't conflict with any flags in notify.h or notify_private.h */ +#define _NOTIFY_COMMON_PORT 0x40000000 /* Notification types */ -#define NOTIFY_TYPE_NONE 0x00000000 -#define NOTIFY_TYPE_MEMORY 0x00000001 -#define NOTIFY_TYPE_PLAIN 0x00000002 -#define NOTIFY_TYPE_PORT 0x00000004 -#define NOTIFY_TYPE_FILE 0x00000008 -#define NOTIFY_TYPE_SIGNAL 0x00000010 -#define NOTIFY_TYPE_MASK 0x000000ff -#define NOTIFY_FLAG_SELF 0x80000000 -#define NOTIFY_FLAG_REGEN 0x40000000 -#define NOTIFY_FLAG_RELEASE_SEND 0x20000000 +#define NOTIFY_TYPE_NONE 0x00000000 +#define NOTIFY_TYPE_MEMORY 0x00000001 +#define NOTIFY_TYPE_PLAIN 0x00000002 +#define NOTIFY_TYPE_PORT 0x00000004 +#define NOTIFY_TYPE_FILE 0x00000008 +#define NOTIFY_TYPE_SIGNAL 0x00000010 +#define NOTIFY_TYPE_MASK 0x0000001f // If this is changed, make sure it doesn't muck with struct client_s +#define NOTIFY_FLAG_SELF 0x80000000 +#define NOTIFY_FLAG_REGEN 0x40000000 +#define NOTIFY_FLAG_RELEASE_SEND 0x20000000 +#define NOTIFY_FLAG_DISPATCH 0x10000000 +#define NOTIFY_TYPE_COALESCE_BASE 0x08000000 +#define NOTIFY_TYPE_COALESCED 0x04000000 +#define NOTIFY_FLAG_RETAINED 0x02000000 +#define NOTIFY_FLAG_CANCELED 0x01000000 + +/* Used to check for a polled or delivered types */ +#define NOTIFY_TYPE_POLLED (NOTIFY_TYPE_MEMORY | NOTIFY_TYPE_PLAIN) +#define NOTIFY_TYPE_DELIVERED (NOTIFY_TYPE_PORT | NOTIFY_TYPE_FILE | NOTIFY_TYPE_SIGNAL) /* Return values for notify_check() */ #define NOTIFY_CHECK_FALSE 0 @@ -83,16 +94,13 @@ extern const char *_notify_shm_id(); #define NOTIFY_SERVICE_DIR_FILE_ADD 0x10 #define NOTIFY_SERVICE_DIR_FILE_DELETE 0x20 -#define NOTIFY_CLIENT_STATE_SUSPENDED 0x00000001 -#define NOTIFY_CLIENT_STATE_PENDING 0x00000002 -#define NOTIFY_CLIENT_STATE_TIMEOUT 0x00000004 +#define NOTIFY_CLIENT_STATE_SUSPENDED 0x00000020 +#define NOTIFY_CLIENT_STATE_PENDING 0x00000040 +#define NOTIFY_CLIENT_STATE_TIMEOUT 0x00000080 -#define NOTIFY_PORT_PROC_TYPE_PORT 0x00000010 -#define NOTIFY_PORT_PROC_TYPE_PROC 0x00000020 -#define NOTIFY_PORT_PROC_TYPE_MASK 0x000000f0 -#define NOTIFY_PORT_PROC_STATE_INVALID 0x00000001 -#define NOTIFY_PORT_PROC_STATE_SUSPENDED 0x00000002 -#define NOTIFY_PORT_PROC_STATE_MASK 0x0000000f +/* port_data_t::flags and proc_data_t::flags */ +#define PORT_PROC_FLAGS_NONE 0x00000000 +#define NOTIFY_PORT_PROC_STATE_SUSPENDED 0x00000001 /* notify state flags */ #define NOTIFY_STATE_USE_LOCKS 0x00000001 @@ -103,63 +111,79 @@ extern const char *_notify_shm_id(); #define FD_NONE -1 #define SLOT_NONE -1 -#define _notify_lib_port_new(A,B,C,D) _notify_lib_port_proc_new(A,B,0,C,D) -#define _notify_lib_proc_new(A,B,C,D) _notify_lib_port_proc_new(A,MACH_PORT_NULL,B,C,D) -#define _notify_lib_port_find(A,B) _notify_lib_port_proc_find(A,B,0) -#define _notify_lib_proc_find(A,B) _notify_lib_port_proc_find(A,MACH_PORT_NULL,B) -#define _notify_lib_port_release(A,B) _notify_lib_port_proc_release(A,B,0) -#define _notify_lib_proc_release(A,B) _notify_lib_port_proc_release(A,MACH_PORT_NULL,B) - typedef struct { + LIST_HEAD(, client_s) subscriptions; char *name; + void *private; uint64_t name_id; + uint64_t state; + uint64_t state_time; uint32_t uid; uint32_t gid; uint32_t access; uint32_t slot; uint32_t refcount; uint32_t val; - uint64_t state; - uint64_t state_time; - void *private; - list_t *subscriptions; + uint32_t postcount; + uint32_t last_hour_postcount; } name_info_t; -typedef struct +typedef union client_delivery_u { - uint64_t client_id; - uint32_t state; - name_info_t *name_info; - uint32_t suspend_count; - uint32_t notify_type; - uint32_t lastval; - mach_port_t port; int fd; - uint32_t send_val; - uint32_t pid; + mach_port_t port; uint32_t sig; - void *private; +} client_delivery_t; + +typedef struct client_s +{ + LIST_ENTRY(client_s) client_subscription_entry; + LIST_ENTRY(client_s) client_pid_entry; + LIST_ENTRY(client_s) client_port_entry; + name_info_t *name_info; + client_delivery_t deliver; + union client_id { + struct { + uint32_t token; + uint32_t pid; + }; + uint64_t hash_key; + } cid; + uint32_t lastval; + uint16_t service_index; + uint8_t suspend_count; + uint8_t state_and_type; } client_t; typedef struct { - uint32_t refcount; + LIST_HEAD(, client_s) clients; + uint32_t port; uint32_t flags; - dispatch_source_t src; -} portproc_data_t; +} port_data_t; typedef struct { + LIST_HEAD(, client_s) clients; + dispatch_source_t src; + uint32_t pid; uint32_t flags; - table_t *name_table; - table_t *name_id_table; - table_t *client_table; - table_t *port_table; - table_t *proc_table; +} proc_data_t; + +typedef struct +{ + /* last allocated name id */ + uint64_t name_id; + table_t name_table; + table_64_t name_id_table; + table_64_t client_table; + table_n_t port_table; + table_n_t proc_table; name_info_t **controlled_name; + uint32_t flags; uint32_t controlled_name_count; - pthread_mutex_t *lock; + os_unfair_lock lock; int sock; uint32_t stat_name_alloc; uint32_t stat_name_free; @@ -169,8 +193,7 @@ typedef struct uint32_t stat_portproc_free; } notify_state_t; -notify_state_t *_notify_lib_notify_state_new(uint32_t flags, uint32_t table_size); -void _notify_lib_notify_state_free(notify_state_t *ns); +void _notify_lib_notify_state_init(notify_state_t * ns, uint32_t flags); uint32_t _notify_lib_post(notify_state_t *ns, const char *name, uint32_t uid, uint32_t gid); uint32_t _notify_lib_post_nid(notify_state_t *ns, uint64_t nid, uid_t uid, gid_t gid); @@ -185,32 +208,19 @@ uint32_t _notify_lib_register_signal(notify_state_t *ns, const char *name, pid_t uint32_t _notify_lib_register_mach_port(notify_state_t *ns, const char *name, pid_t pid, int token, mach_port_t port, uint32_t uid, uint32_t gid, uint64_t *out_nid); uint32_t _notify_lib_register_file_descriptor(notify_state_t *ns, const char *name, pid_t pid, int token, int fd, uint32_t uid, uint32_t gid, uint64_t *out_nid); -uint32_t _notify_lib_get_owner(notify_state_t *ns, const char *name, uint32_t *uid, uint32_t *gid); -uint32_t _notify_lib_get_access(notify_state_t *ns, const char *name, uint32_t *access); - uint32_t _notify_lib_set_owner(notify_state_t *ns, const char *name, uint32_t uid, uint32_t gid); uint32_t _notify_lib_set_access(notify_state_t *ns, const char *name, uint32_t access); -uint32_t _notify_lib_release_name(notify_state_t *ns, const char *name, uint32_t uid, uint32_t gid); +void _notify_lib_resume_client(notify_state_t *ns, client_t *c, proc_data_t *proc_data, port_data_t *port_data); +void _notify_lib_cancel_client(notify_state_t *ns, client_t *c); void _notify_lib_cancel(notify_state_t *ns, pid_t pid, int token); -void _notify_lib_suspend(notify_state_t *ns, pid_t pid, int token); +uint32_t _notify_lib_suspend(notify_state_t *ns, pid_t pid, int token); uint32_t _notify_lib_resume(notify_state_t *ns, pid_t pid, int token); -void _notify_lib_cancel_proc(notify_state_t *ns, pid_t pid); -void _notify_lib_suspend_proc(notify_state_t *ns, pid_t pid); -void _notify_lib_resume_proc(notify_state_t *ns, pid_t pid); - -void _notify_lib_suspend_port(notify_state_t *ns, mach_port_t port); -void _notify_lib_resume_port(notify_state_t *ns, mach_port_t port); - uint32_t _notify_lib_check_controlled_access(notify_state_t *ns, char *name, uid_t uid, gid_t gid, int req); uint64_t make_client_id(pid_t pid, int token); -uint32_t _notify_lib_port_proc_new(notify_state_t *ns, mach_port_t port, pid_t proc, uint32_t state, dispatch_source_t src); -portproc_data_t *_notify_lib_port_proc_find(notify_state_t *ns, mach_port_t port, pid_t proc); -void _notify_lib_port_proc_release(notify_state_t *ns, mach_port_t port, pid_t proc); - #endif /* _LIBNOTIFY_H_ */ diff --git a/src/libnotify/notify.3 b/src/libnotify/notify.3 index af4b88e0a..259f0944d 100644 --- a/src/libnotify/notify.3 +++ b/src/libnotify/notify.3 @@ -46,6 +46,8 @@ .Fn notify_post "const char *name" .Ft uint32_t .Fn notify_register_check "const char *name, int *out_token" +.Ft void +.Fn (^notify_handler_t) "int token" .Ft uint32_t .Fn notify_register_dispatch "const char *name, int *out_token" "dispatch_queue_t queue" "notify_handler_t handler" .Ft uint32_t @@ -142,13 +144,6 @@ registers a client for notification delivery via a signal. This fits well with the design of many UNIX daemons that use a signal such as SIGHUP to reinitialize of reset internal state information. -Clients may use the -registration token generated by this routine to check for notifications using -.Fn notify_check . -This allows the application to determine if a signal was received as the -result of a notification, or if the signal was generated by some other source. -It also permits the application that registers for signal notification for -multiple names to determine which name was associated with the notification. .Ss notify_register_mach_port registers a client for notification delivery via mach messaging. Notifications are delivered by an empty message sent to a mach port. @@ -160,6 +155,8 @@ set in the flags parameter. The notification service must be able to extract send rights to the port. .Pp +Values for the flags parameter may only be 0 (zero) or NOTIFY_REUSE. +.Pp Note that the kernel limits the size of the message queue for any port. If it is important that notifications should not be lost due to queue overflow, clients should service messages quickly, and be cautious in @@ -180,7 +177,9 @@ is returned as the value of the "notify_fd" parameter. A file descriptor created by a previous call to this routine may be used for notifications if a pointer to that file descriptor is passed in to the routine and -NOTIFY_REUSE is set in the flags parameter. +NOTIFY_REUSE is set in the flags parameter. +.Pp +Values for the flags parameter may only be 0 (zero) or NOTIFY_REUSE. .Pp Note that the kernel limits the buffer space for queued writes on a file descriptor. @@ -254,6 +253,60 @@ the port or file descriptor have been cancelled. Determines if an integer value is valid for a current registration. Negative integers are never valid. A positive or zero value is valid if the current process has a registration associated with the given value. + +.Sh RETURN VALUES +Many notify functions return status (uint32_t) to indicate success or failure. +This will always be one of the following: + +.Ss NOTIFY_STATUS_OK +The function did not encounter any issues. + +.Ss NOTIFY_STATUS_INVALID_NAME +Name argument is not valid. +Often this will indicate that the name passed to the function is NULL. + +.Ss NOTIFY_STATUS_INVALID_TOKEN +The function expected a valid token, given by a notify_register_* function, and was passed an invalid token. +Token validity can by checked with +.Fn notify_is_valid_token . + +.Ss NOTIFY_STATUS_INVALID_PORT +The function is not able to use the port passed. +This may be because the port is NULL, MACH_PORT_NULL, MACH_PORT_DEAD, or the calling process does not have the +correct rights to the port. + +.Ss NOTIFY_STATUS_INVALID_FILE +The function was passed NULL, or something that is not a file descriptor generated by notify_register_file_descriptor. + +.Ss NOTIFY_STATUS_INVALID_SIGNAL +Legacy, currently unused. + +.Ss NOTIFY_STATUS_INVALID_REQUEST +An internal error occurred. + +.Ss NOTIFY_STATUS_NOT_AUTHORIZED +The calling process is not authorized to take this action. +Usually this indicates that the calling process may not act on the name given. + +.Ss NOTIFY_STATUS_OPT_DISABLE +An internal error occurred. + +.Ss NOTIFY_STATUS_SERVER_NOT_FOUND +The server could not be found. +This usually indicates that sandboxing is preventing the calling process from accessing notifyd. + +.Ss NOTIFY_STATUS_NULL_INPUT +One of the inputs was called with NULL when it must not be NULL. +For legacy support, if name, token, port, or file descriptor is NULL, the respective NOTIFY_STATUS_INVALID_* return code +will be used instead. + +.Ss NOTIFY_STATUS_FAILED +Indicates an internal failure of the library. +The caller may try again; another attempt may be successful. +Please report any instances where this is returned. + + + .Sh NAMESPACE CONVENTIONS Names in the namespace must be NULL-terminated. Names should be encoded as UTF-8 strings. diff --git a/src/libnotify/notify.h b/src/libnotify/notify.h index 2d1a219c1..275c0b7d8 100644 --- a/src/libnotify/notify.h +++ b/src/libnotify/notify.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #ifdef __BLOCKS__ #include @@ -75,7 +76,7 @@ */ /*! @defineblock Status Codes - * Status codes returned by the API. + * Status codes returned by the API. See notify(3) for detailed description. */ #define NOTIFY_STATUS_OK 0 #define NOTIFY_STATUS_INVALID_NAME 1 @@ -85,7 +86,12 @@ #define NOTIFY_STATUS_INVALID_SIGNAL 5 #define NOTIFY_STATUS_INVALID_REQUEST 6 #define NOTIFY_STATUS_NOT_AUTHORIZED 7 +#define NOTIFY_STATUS_OPT_DISABLE 8 +#define NOTIFY_STATUS_SERVER_NOT_FOUND 9 +#define NOTIFY_STATUS_NULL_INPUT 10 + #define NOTIFY_STATUS_FAILED 1000000 + /*! @/defineblock */ /*! @@ -110,7 +116,7 @@ __BEGIN_DECLS * This is the only call that is required for a notification producer. * Returns status. */ -uint32_t notify_post(const char *name); +OS_EXPORT uint32_t notify_post(const char *name); #ifdef __BLOCKS__ @@ -131,13 +137,13 @@ typedef void (^notify_handler_t)(int token); * The dispatch queue is retained by the notify subsystem while * the notification is registered, and will be released when * notification is canceled. - * @param block (input) The Block to invoke on the dispatch queue in response + * @param handler (input) The Block to invoke on the dispatch queue in response * to a notification. The notification token is passed to the * Block as an argument so that the callee can modify the state * of the notification or cancel the registration. * @result Returns status. */ -uint32_t notify_register_dispatch(const char *name, int *out_token, dispatch_queue_t queue, notify_handler_t handler) +OS_EXPORT uint32_t notify_register_dispatch(const char *name, int *out_token, dispatch_queue_t queue, notify_handler_t handler) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_3_2); #endif /* __BLOCKS__ */ @@ -151,7 +157,7 @@ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_3_2); * (output) registration token * @result Returns status. */ -uint32_t notify_register_check(const char *name, int *out_token); +OS_EXPORT uint32_t notify_register_check(const char *name, int *out_token); /*! * Request notification delivery by UNIX signal. @@ -165,7 +171,7 @@ uint32_t notify_register_check(const char *name, int *out_token); * @param out_token (output) notification token * @result Returns status. */ -uint32_t notify_register_signal(const char *name, int sig, int *out_token); +OS_EXPORT uint32_t notify_register_signal(const char *name, int sig, int *out_token); /*! * Request notification by mach message. @@ -197,9 +203,9 @@ uint32_t notify_register_signal(const char *name, int sig, int *out_token); * (input/output) pointer to a mach port * @result Returns status. */ -uint32_t notify_register_mach_port(const char *name, mach_port_t *notify_port, int flags, int *out_token); +OS_EXPORT uint32_t notify_register_mach_port(const char *name, mach_port_t *notify_port, int flags, int *out_token); -/* +/*! * Request notification by a write to a file descriptor. * * Notifications are delivered by a write to a file descriptor. @@ -227,7 +233,7 @@ uint32_t notify_register_mach_port(const char *name, mach_port_t *notify_port, i * (input/output) pointer to a file descriptor * @result Returns status. */ -uint32_t notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int *out_token); +OS_EXPORT uint32_t notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int *out_token); /*! * Check if any notifications have been posted. @@ -246,7 +252,7 @@ uint32_t notify_register_file_descriptor(const char *name, int *notify_fd, int f * (output) true/false indication * @result Returns status. */ -uint32_t notify_check(int token, int *check); +OS_EXPORT uint32_t notify_check(int token, int *check); /*! * Cancel notification and free resources associated with a notification @@ -258,7 +264,7 @@ uint32_t notify_check(int token, int *check); * (input) notification token * @result Returns status. */ -uint32_t notify_cancel(int token); +OS_EXPORT uint32_t notify_cancel(int token); /*! * Suspend delivery of notifications for a token. Notifications for this token will be @@ -270,7 +276,7 @@ uint32_t notify_cancel(int token); * (input) notification token * @result Returns status. */ -uint32_t notify_suspend(int token) +OS_EXPORT uint32_t notify_suspend(int token) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0); /*! @@ -284,7 +290,7 @@ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0); * (input) notification token * @result Returns status. */ -uint32_t notify_resume(int token) +OS_EXPORT uint32_t notify_resume(int token) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0); /*! @@ -305,7 +311,7 @@ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0); * (input) 64-bit unsigned integer value * @result Returns status. */ -uint32_t notify_set_state(int token, uint64_t state64) +OS_EXPORT uint32_t notify_set_state(int token, uint64_t state64) __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0); /*! @@ -317,20 +323,20 @@ __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0); * (output) 64-bit unsigned integer value * @result Returns status. */ -uint32_t notify_get_state(int token, uint64_t *state64) +OS_EXPORT uint32_t notify_get_state(int token, uint64_t *state64) __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0); /*! * Determine if a token is valid (currently registered). * Negative integer values are always invalid. Positive or * zero values are valid only if they are associated with an - * existing registratiom. + * existing registration. * * @param val * (input) integer value * @result Returns true if the value is a valid token, false otherwise. */ -bool notify_is_valid_token(int val) +OS_EXPORT bool notify_is_valid_token(int val) __OSX_AVAILABLE_STARTING(__MAC_10_10,__IPHONE_8_0); __END_DECLS diff --git a/src/libnotify/notify_client.c b/src/libnotify/notify_client.c index 290be04e8..5d4ce65da 100644 --- a/src/libnotify/notify_client.c +++ b/src/libnotify/notify_client.c @@ -41,26 +41,42 @@ #include #include #include -#include +#include +#include +#include +#include +#include #include #include -#include #include #include #include +#include #include <_simple.h> +#include // _dyld_is_memory_immutable + #include "libnotify.h" #include "notify.h" #include "notify_internal.h" #include "notify_ipc.h" #include "notify_private.h" +#include "notify_probes.h" -#define INITIAL_TOKEN_ID 0 - -// -WEAK_IMPORT_ATTRIBUTE bool _dispatch_is_multithreaded(void); +#ifdef DEBUG +#define DEBUG_REGISTRATION 0x00000001 +#define DEBUG_NOTIFICATION 0x00000002 +#define DEBUG_RETAIN_RELEASE 0x00000004 +#define DEBUG_CANCEL 0x00000008 +#define DEBUG_GET_STATE 0x00000010 +#define DEBUG_SEND_NO_BLOCK 0x00000020 +#define DEBUG_NODES 0x00000040 +#define DEBUG_API 0x00000080 +#define DEBUG_USER 0x80000000 +#define DEBUG_ALL 0xffffffff +static uint32_t _libnotify_debug = DEBUG_ALL; +#endif /* DEBUG */ #define EVENT_INIT 0 #define EVENT_REGEN 1 @@ -70,138 +86,713 @@ WEAK_IMPORT_ATTRIBUTE bool _dispatch_is_multithreaded(void); #define COMMON_SELF_PORT_KEY "self.com.apple.system.notify.common" -#define MULTIPLE_REGISTRATION_WARNING_TRIGGER 20 - -extern uint32_t _notify_lib_peek(notify_state_t *ns, pid_t pid, int token, int *val); -extern int *_notify_lib_check_addr(notify_state_t *ns, pid_t pid, int token); - -#define CLIENT_TOKEN_TABLE_SIZE 256 +#define MULTIPLE_REGISTRATION_WARNING_TRIGGER 500 #define NID_UNSET 0xffffffffffffffffL #define NID_CALLED_ONCE 0xfffffffffffffffeL -#define NO_LOCK 1 +/* + * Details about registrations, tokens, dispatch (NOTIFY_OPT_DISPATCH), IPC versions, and etc. + * + * In the first versions of the client/server protocol (ipc versions 0 and 1), the integer + * token representing a registration was generated by notifyd and returned to the client + * as an out parameter in the MIG registration call. + * + * If a client uses the old protocols, then each registration in the client goes straight to + * the server. If the client registers for signals, file descriptor writes, or whatever, + * that's all handled by the server. + * + * The current version (ipc version 2) has better performance. The client now generates + * a unique token (simply incremented by 1 for each new registration) and sends it to + * notifyd in a MIG simpleroutine. notifyd internally uses a 64 bit ID for each registration + * that's formed from the client's PID (high-order 32 bits) and the client-provided integer + * (low-order 32 bits). + * + * With the advent of libdispatch, there was an opportunity to further improve performance + * and reduce the load on notifyd. The client library (this source file) checks if the + * client process is multithreaded, or at least can be multithreaded, with a call to the + * _dispatch_is_multithreaded() routine. This tells us if we can use dispatch in the client. + * + * If the client can use dispatch (NOTIFY_OPT_DISPATCH gets set in globals->client_opts), + * then all registrations use a single shared mach port (globals->notify_common_port). + * The client creates a dispatch source (globals->notify_dispatch_source) that handles all + * notifications from the server. The handler routine looks up the client's registration + * for the received notification and sends a signal, writes on a file descriptor, or sends + * a mach message as required. It's all done locally in the client process. + * + * Many clients register several times for the same notification name. That's sometimes + * due to bad code, but it may be legitimate as well. For example, different libraries or + * frameworks may register independently, or different threads in a client process may + * each require a registration for the same name. When dispatch is available, client + * registrations for the same name are coalesced. The library still generates a new + * token (and an underlying token data structure) for each registration, but only the + * first registration is actually sent to notifyd. Subsequent registrations are simply + * chained to the first in a linked list. When the globals->notify_dispatch_source + * handler processes a notification from the server, it traverses the linked list and + * forwards the notification to each. + * + * Note that it is possible for the client to still have multiple registrations with + * notifyd, even when coalescing. Polled registrations (memory or plain) must be handled + * by notifyd, so these registration types are not coalesced. Also, a client might start + * out single threaded and appear not to be dispatch-safe, but them become multi-threaded + * later on. Early registrations would go individually to notifyd, while later + * registrations would be coalesced. + * + * The library uses both locking and refcounting of data structures. A mutex in the + * library globals (globals->notify_lock) is used when accessing mutable values, hash + * tables, and lists that are found in the global data (returned by _notify_globals()). + * Data structure instances (name_node_t and registration_node_t types) are refcounted. + * name_node_t instances have locks to protect their data. registration_node_t + * instances do not, mostly to save memory. Most operations on them can either be done + * atomically, or are within the scope of some other lock. + */ typedef struct { - uint32_t refcount; uint64_t name_id; -} name_table_node_t; + TAILQ_HEAD(, __registration_node_s) coalesced; + struct __registration_node_s *coalesce_base; + char *name; + os_unfair_lock lock; + atomic_uint_fast32_t refcount; + uint32_t coalesce_base_token; + bool has_been_warned; + bool needs_free; +} name_node_t; -typedef struct +/* + * Data structure behind a client's token. + * The library exports tokens (integers) to users of the library, so + * notify_register_...() gives the client an int value that represents + * a registration. + * + * If the client registers multiple times for the same name and we can + * use dispatch in the library, then the duplicate registration creates + * a new client-side registration, but not a new registration with the + * server. Multiple registrations are chained in a linked list. The + * base registration is retained for each coalesced registration. + */ +typedef struct __registration_node_s { - uint32_t refcount; - const char *name; - size_t namelen; - name_table_node_t *name_node; + TAILQ_ENTRY(__registration_node_s) registration_coalesced_entry; + + atomic_uint_fast32_t refcount; uint32_t token; + uint32_t flags; + + /* shared memory slot and value at that location when we last did notify_check() */ uint32_t slot; uint32_t val; - uint32_t flags; + + /* client-facing parts of a notification */ int fd; int signal; mach_port_t mp; - uint32_t client_id; - uint64_t set_state_time; - uint64_t set_state_val; - char * path; - int path_flags; dispatch_queue_t queue; notify_handler_t block; -} token_table_node_t; + + /* client_id is the value returned from notifyd for registrations using IPC version 0 */ + uint32_t client_id; + + /* state value and timestamp when we set it - used to regenerate if notifyd restarts */ + uint64_t set_state_val; + uint64_t set_state_time; + + /* path monitoring */ + char *path; + int path_flags; + + /* name table node for this registration */ + name_node_t *name_node; +} registration_node_t; + /* FORWARD */ -static void _notify_lib_regenerate(int src); -static void notify_retain_mach_port(mach_port_t mp, int mine); -static void _notify_dispatch_handle(mach_port_t port); -static notify_state_t *_notify_lib_self_state(); +static void _notify_lib_server_restart_handler(void *ctxt); +static void notify_retain_mach_port(notify_globals_t globals, mach_port_t mp, int flags); +static void _notify_dispatch_handle(void *context); +static void registration_node_release(registration_node_t *r); +static void registration_node_release_locked(notify_globals_t globals, registration_node_t *r); +static void notify_release_file_descriptor_locked(notify_globals_t globals, int fd); +static void notify_release_mach_port_locked(notify_globals_t globals, mach_port_t mp, uint32_t flags); -#if TARGET_IPHONE_SIMULATOR -const char * -_notify_shm_id() +// TSAN doesn't know about os_unfair_lock_with_options +#if defined(__has_feature) +#if __has_feature(thread_sanitizer) +#define TSAN_SAFE_LOCK(x) os_unfair_lock_lock(x) +#else +#define TSAN_SAFE_LOCK(x) os_unfair_lock_lock_with_options(x, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION) +#endif // __has_feature(thread_sanitizer) +#else +#define TSAN_SAFE_LOCK(x) os_unfair_lock_lock(x) +#endif // defined(__clang__) + +#ifdef DEBUG_MUTEX +#define mutex_lock(s,x,f,l) \ +_notify_client_log(ASL_LEVEL_NOTICE, "attempting mutex lock %s %p from %s:%u", s, x, f, l); \ +TSAN_SAFE_LOCK(x); \ +_notify_client_log(ASL_LEVEL_NOTICE, "acquired mutex lock %s %p from %s:%u", s, x, f, l); +#define mutex_unlock(s,x,f,l) \ +_notify_client_log(ASL_LEVEL_NOTICE, "dropping mutex lock %s %p from %s:%u", s, x, f, l); \ +os_unfair_lock_unlock(x); +#else +#define mutex_lock(s,x,f,l) TSAN_SAFE_LOCK(x) +#define mutex_unlock(s,x,f,l) os_unfair_lock_unlock(x) +#endif + +// returns the result after the decrement +inline static int32_t +atomic_refcount_release(atomic_uint_fast32_t *val) { - static dispatch_once_t once; - static char *shm_id; + int32_t result = atomic_fetch_sub_explicit(val, 1, memory_order_release); + // Crash right away if we underrun our refcounts + // as this indicates a bug in our logic + assert(result > 0); + // result == 1 means that this was the last reference so any changes + // made to the refcounted object need to be visible to all threads + // at this point. c.f. "Release-Acquire ordering" at + // http://en.cppreference.com/w/cpp/atomic/memory_order + if (result == 1) atomic_thread_fence(memory_order_acquire); + return result - 1; +} - dispatch_once(&once, ^{ - // According to documentation, our shm_id must be no more than 31 characters long - // but in practice, even 31 characters is too long (), - // so we jump through some hoops to make a smaller string based on our UDID. - const char *udid = getenv("SIMULATOR_UDID"); - if (udid && strlen(udid) == 36) { - char scratch[34]; // 32 characters, 2 NUL +// returns the result after the increment +inline static int32_t +atomic_increment32(atomic_uint_fast32_t *val) +{ + int32_t result = atomic_fetch_add_explicit(val, 1, memory_order_relaxed); + return result + 1; +} - // 01234567890123456789012345678901234567890 - // UUUUUUUU-UUUU-UUUU-LLLL-LLLLLLLLLLLL - memcpy(scratch, udid, 8); - memcpy(scratch+8, udid+9, 4); - memcpy(scratch+12, udid+14, 4); - scratch[16] = '\0'; +inline static void +name_node_retain(name_node_t *node) +{ + atomic_increment32(&node->refcount); +} - memcpy(scratch+17, udid+19, 4); - memcpy(scratch+21, udid+24, 12); - scratch[33] = '\0'; +inline static void +registration_node_retain(registration_node_t *reg) +{ + atomic_increment32(®->refcount); +} - // If the input is invalid, these will end up being undefined - // values, but they'll still be values we can use. - uint64_t upper = strtoull(scratch, NULL, 16); - uint64_t lower = strtoull(scratch + 17, NULL, 16); +__printflike(2, 3) +static void +_notify_client_log(int level, const char *fmt, ...) +{ + va_list ap; + char *msg = NULL; - const char *c64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; - scratch[0] = c64[(upper >> 57) & 0xf]; - scratch[1] = c64[(upper >> 50) & 0xf]; - scratch[2] = c64[(upper >> 43) & 0xf]; - scratch[3] = c64[(upper >> 36) & 0xf]; - scratch[4] = c64[(upper >> 29) & 0xf]; - scratch[5] = c64[(upper >> 22) & 0xf]; - scratch[6] = c64[(upper >> 15) & 0xf]; - scratch[7] = c64[(upper >> 8) & 0xf]; - scratch[8] = c64[(upper >> 1) & 0xf]; - // Drop a bit on the floor, but that probably doesn't matter. It does not need to be reversible + va_start(ap, fmt); + vasprintf(&msg, fmt, ap); + va_end(ap); - scratch[10] = c64[(lower >> 57) & 0xf]; - scratch[11] = c64[(lower >> 50) & 0xf]; - scratch[12] = c64[(lower >> 43) & 0xf]; - scratch[13] = c64[(lower >> 36) & 0xf]; - scratch[14] = c64[(lower >> 29) & 0xf]; - scratch[15] = c64[(lower >> 22) & 0xf]; - scratch[16] = c64[(lower >> 15) & 0xf]; - scratch[17] = c64[(lower >> 8) & 0xf]; - scratch[18] = c64[(lower >> 1) & 0xf]; - // Drop a bit on the floor, but that probably doesn't matter. It does not need to be reversible + if (msg != NULL) + { + _simple_asl_log(level, "com.apple.notify", msg); +#ifdef DEBUG_VERBOSE + fprintf(stderr, "thread %p: %s\n", (void *)pthread_self(), msg); +#endif + } - scratch[19] = '\0'; + free(msg); +} - asprintf(&shm_id, "sim.not.%s", scratch); - assert(shm_id); + +#if !TARGET_OS_SIMULATOR && !TARGET_OS_OSX + +static const uint32_t LAUNCHD_PID = 1; + +// do this so we don't have to link against libtrace +__printflike(1, 2) +static void +simulate_crash(char *fmt, ...) +{ + char *desc = NULL; + va_list ap; + int len; + + if (getpid() == LAUNCHD_PID) { + return; + } + + va_start(ap, fmt); + len = vasprintf(&desc, fmt, ap); + va_end(ap); + + if (desc == NULL) { + return; + } + + os_fault_with_payload(OS_REASON_LIBSYSTEM, OS_REASON_LIBSYSTEM_CODE_FAULT, + desc, len + 1, desc, 0); + free(desc); +} + +#define REPORT_BAD_BEHAVIOR(...) \ + if(os_variant_has_internal_diagnostics("libnotify.simulate_crash")) \ + { \ + simulate_crash(__VA_ARGS__); \ + } else { \ + _notify_client_log(ASL_LEVEL_ERR, __VA_ARGS__); \ + } + +#else /* !TARGET_OS_SIMULATOR && !TARGET_OS_OSX */ + +#define REPORT_BAD_BEHAVIOR(...) _notify_client_log(ASL_LEVEL_ERR, __VA_ARGS__) + +#endif /* !TARGET_OS_SIMULATOR && !TARGET_OS_OSX */ + +static char * +_notify_strdup_if_mutable(const char *str, bool *needs_free) +{ + size_t size = strlen(str) + 1; + if (!_dyld_is_memory_immutable(str, size)) { + char *clone = (char *)malloc(size); + if (clone) { + memcpy(clone, str, size); + if (needs_free) *needs_free = true; } + return clone; + } + if (needs_free) *needs_free = false; + return (char *)str; +} - if (!shm_id) { - shm_id = "apple.shm.notification_center"; - } - }); +#pragma mark - +#pragma mark globals - return shm_id; +#define INITIAL_TOKEN_ID 1 + +/* + * Initialization of global variables. Called once per process. + */ +static void +_notify_init_globals(void * /* notify_globals_t */ _globals) +{ + notify_globals_t globals = _globals; + + globals->notify_lock = OS_UNFAIR_LOCK_INIT; + globals->token_id = INITIAL_TOKEN_ID; + globals->notify_common_token = -1; + globals->check_lock = OS_UNFAIR_LOCK_INIT; + _nc_table_init(&globals->name_node_table, offsetof(name_node_t, name)); + _nc_table_init_n(&globals->registration_table, offsetof(registration_node_t, token)); + + _notify_lib_notify_state_init(&globals->self_state, NOTIFY_STATE_USE_LOCKS); +} + +__attribute__((__pure__)) +static inline notify_globals_t +_notify_globals(void) +{ + return (notify_globals_t)os_alloc_once(OS_ALLOC_ONCE_KEY_LIBSYSTEM_NOTIFY, + sizeof(struct notify_globals_s), &_notify_init_globals); +} + +#pragma mark - +#pragma mark name_node_t + +#ifdef NOTDEF +static void +name_node_dump(int level, name_node_t *n) +{ + if (n == NULL) + { + _notify_client_log(level, "name_node_t NULL\n"); + return; + } + + _notify_client_log(level, "name_node_t %p name=%s name_id=%llu refcount=%d coalesce_base_token=%u coalesce_base=%p\n", (n->name == NULL) ? "NULL" : n->name, n->name_id, n->refcount, n->coalesce_base_token, n->coalesce_base); } #endif -static int +// must be called with the global lock held +static name_node_t * +name_node_for_name_locked(notify_globals_t globals, const char *name, uint64_t nid, bool create) +{ + os_unfair_lock_assert_owner(&globals->notify_lock); + + if (name == NULL) return NULL; + + name_node_t *n = _nc_table_find(&globals->name_node_table, name); + if (n != NULL) + { + name_node_retain(n); + } + else if (create) + { + n = (name_node_t *)calloc(1, sizeof(name_node_t)); + if (n == NULL) + { +#ifdef DEBUG + _notify_client_log(ASL_LEVEL_ERR, "name_node_for_name name %s calloc failed errno %d [%s]\n", name, errno, strerror(errno)); +#endif + goto done; + } + + n->name = _notify_strdup_if_mutable(name, &n->needs_free); + if (n->name == NULL) + { + free(n); + n = NULL; + goto done; + } + + n->refcount = 1; + n->name_id = nid; + TAILQ_INIT(&n->coalesced); + n->coalesce_base_token = NOTIFY_TOKEN_INVALID; + n->lock = OS_UNFAIR_LOCK_INIT; + n->has_been_warned = false; + + _nc_table_insert(&globals->name_node_table, &n->name); + } + +done: + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NODES) + { + if (n == NULL) _notify_client_log(ASL_LEVEL_NOTICE, "name_node_for_name name %s returning NULL\n", name); + else _notify_client_log(ASL_LEVEL_NOTICE, "name_node_for_name name %s refcount %d %p\n", n->name, n->refcount, n); + } +#endif + + return n; +} + +static name_node_t * +name_node_for_name(const char *name, uint64_t nid, bool create) +{ + notify_globals_t globals = _notify_globals(); + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); + name_node_t *node = name_node_for_name_locked(globals, name, nid, create); + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + return node; +} + +// must be called with the global lock held +static void +name_node_delete_locked(notify_globals_t globals, name_node_t *n) +{ + os_unfair_lock_assert_owner(&globals->notify_lock); + + /* refcount is zero, free the node */ +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "name_node_release name %s refcount %d %p FREE", n->name, n->refcount, n); +#endif + + _nc_table_delete(&globals->name_node_table, n->name); + if (n->needs_free) { + free(n->name); + } + n->name = NULL; + free(n); +} + +// must be called with the global lock held +static void +name_node_release_locked(notify_globals_t globals, name_node_t *n) +{ + os_unfair_lock_assert_owner(&globals->notify_lock); + + if (n == NULL) return; + + if (atomic_refcount_release(&n->refcount) > 0) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "%s name %s refcount %d %p", __func__, n->name, n->refcount, n); +#endif + return; + } + name_node_delete_locked(globals, n); +} + +static void +name_node_unlock_and_release(name_node_t *n) +{ + if (n == NULL) return; + + mutex_unlock(n->name, &n->lock, __func__, __LINE__); + notify_globals_t globals = _notify_globals(); + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); + if (atomic_refcount_release(&n->refcount) > 0) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "%s name %s refcount %d %p", __func__, n->name, n->refcount, n); +#endif + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + return; + } + name_node_delete_locked(globals, n); + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); +} + +// We avoid an extra retain here if the base registration was just created +static void +name_node_add_coalesced_registration_locked(name_node_t *n, registration_node_t *r, bool skip_retain) +{ + if (n == NULL) return; + if (r == NULL) return; + + os_unfair_lock_assert_owner(&n->lock); + + if (!skip_retain && n->coalesce_base) registration_node_retain(n->coalesce_base); + TAILQ_INSERT_TAIL(&n->coalesced, r, registration_coalesced_entry); +} + +// must be called with the global lock held +static void +name_node_remove_coalesced_registration_locked(notify_globals_t globals, name_node_t *n, registration_node_t *r) +{ + os_unfair_lock_assert_owner(&globals->notify_lock); + + if (n == NULL) return; + if (r == NULL) return; + + mutex_lock(n->name, &n->lock, __func__, __LINE__); + TAILQ_REMOVE(&n->coalesced, r, registration_coalesced_entry); + mutex_unlock(n->name, &n->lock, __func__, __LINE__); + + registration_node_release_locked(globals, n->coalesce_base); +} + +static void +name_node_set_nid_locked(name_node_t *n, uint64_t nid) +{ + os_unfair_lock_assert_owner(&n->lock); + n->name_id = nid; +} + +static void +name_node_set_nid(name_node_t *n, uint64_t nid) +{ + if (n == NULL) return; + + mutex_lock(n->name, &n->lock, __func__, __LINE__); + name_node_set_nid_locked(n, nid); + mutex_unlock(n->name, &n->lock, __func__, __LINE__); +} + +#pragma mark - +#pragma mark registration_node_t + +static registration_node_t * +registration_node_find(uint32_t token) +{ + notify_globals_t globals = _notify_globals(); + + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); + registration_node_t *r = _nc_table_find_n(&globals->registration_table, token); + if (r != NULL) registration_node_retain(r); + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "registration_node_find token %u refcount %d -> %p", token, r ? r->refcount : -1, r); +#endif + + return r; +} + +/* + * Tells the caller if a value is a valid token number. + * Internal coalesce_base registration tokens are reported as invalid, + * since clients should not be mucking around with them. + */ +bool +notify_is_valid_token(int val) +{ +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + bool valid = true; + notify_globals_t globals = _notify_globals(); + + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); + registration_node_t *r = _nc_table_find_n(&globals->registration_table, val); + if (r == NULL) valid = false; + else if (r->flags & NOTIFY_TYPE_COALESCE_BASE) valid = false; + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return valid; +} + +// must be called with the global lock held +static void +registration_node_free_locked(notify_globals_t globals, registration_node_t *r) +{ + os_unfair_lock_assert_owner(&globals->notify_lock); + + name_node_t *n = r->name_node; + + if (r->flags & NOTIFY_TYPE_COALESCED) + { + name_node_remove_coalesced_registration_locked(globals, n, r); + } + else if (r->flags & NOTIFY_TYPE_COALESCE_BASE) + { + mutex_lock(n->name, &n->lock, __func__, __LINE__); + n->coalesce_base_token = NOTIFY_TOKEN_INVALID; + n->coalesce_base = NULL; + mutex_unlock(n->name, &n->lock, __func__, __LINE__); + + /* cancel the registration with notifyd */ +#ifdef DEBUG + if (_libnotify_debug & DEBUG_CANCEL) _notify_client_log(ASL_LEVEL_NOTICE, "_notify_server_cancel_2 token %u", r->token); +#endif + // hold lock across async server call + (void)_notify_server_cancel_2(globals->notify_server_port, r->token); + } + + notify_release_file_descriptor_locked(globals, r->fd); + notify_release_mach_port_locked(globals, r->mp, r->flags); + free(r->path); + + if (r->block != NULL) dispatch_async_f(r->queue, r->block, (dispatch_function_t)_Block_release); + r->block = NULL; + + if (r->queue != NULL) dispatch_release(r->queue); + r->queue = NULL; + + free(r); + + name_node_release_locked(globals, n); +} + +// must be called with the global lock held +static void +registration_node_delete_locked(notify_globals_t globals, registration_node_t *r) +{ + os_unfair_lock_assert_owner(&globals->notify_lock); + + /* refcount is zero, free the node */ + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "%s token %u refcount %d flags 0x%08x %p FREE", __func__, r->token, r->refcount, r->flags, r); +#endif + + _nc_table_delete_n(&globals->registration_table, r->token); + + uint32_t reg_token = r->token; + uint32_t reg_flags = r->flags; + registration_node_free_locked(globals, r); + + if (reg_flags & NOTIFY_FLAG_SELF) + { + /* + * _notify_lib_cancel fails quietly if self_state is NULL + * We let it fail quietly. + */ + _notify_lib_cancel(&globals->self_state, NOTIFY_CLIENT_SELF, reg_token); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return; + } + + if (reg_flags & NOTIFY_TYPE_COALESCE_BASE) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_CANCEL) _notify_client_log(ASL_LEVEL_NOTICE, "notify_cancel token %d NOTIFY_TYPE_COALESCE_BASE", reg_token); +#endif +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return; + } + kern_return_t kstatus = KERN_SUCCESS; + if ((reg_flags & NOTIFY_TYPE_COALESCED) == 0) + { + // hold lock across async server call + kstatus = _notify_server_cancel_2(globals->notify_server_port, reg_token); + } + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_CANCEL) _notify_client_log(ASL_LEVEL_NOTICE, "notify_cancel token %d reg_node %p has been cancelled", reg_token, r); +#endif + + if ((kstatus == MIG_SERVER_DIED) || (kstatus == MACH_SEND_INVALID_DEST)) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return; + } + else if (kstatus != KERN_SUCCESS) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + _notify_client_log(ASL_LEVEL_ERR, "<- %s [%d] _notify_server_cancel_2 failed: 0x%08x\n", __func__, __LINE__ + 2, kstatus); + return; + } + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif +} + +static void +registration_node_release(registration_node_t *r) +{ + notify_globals_t globals = _notify_globals(); + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); + registration_node_release_locked(globals, r); + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); +} + +static void +registration_node_release_locked(notify_globals_t globals, registration_node_t *r) +{ + os_unfair_lock_assert_owner(&globals->notify_lock); + + if (r == NULL) return; + + if (atomic_refcount_release(&r->refcount) > 0) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "%s token %u refcount %d flags 0x%08x %p", __func__, r->token, r->refcount, r->flags, r); +#endif + + return; + } + registration_node_delete_locked(globals, r); +} + +#if !TARGET_OS_SIMULATOR +static bool shm_attach(uint32_t size) { int32_t shmfd; notify_globals_t globals = _notify_globals(); + void *shm_base = NULL; + bool result = true; shmfd = shm_open(SHM_ID, O_RDONLY, 0); - if (shmfd == -1) return -1; + if (shmfd == -1) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed on line %d with errno %d", __func__, __LINE__, errno); + return false; + } + + shm_base = mmap(NULL, size, PROT_READ, MAP_SHARED, shmfd, 0); + if (shm_base == MAP_FAILED) { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed on line %d with errno %d", __func__, __LINE__, errno); + result = false; + } else { + globals->shm_base = shm_base; + result = true; + } - globals->shm_base = mmap(NULL, size, PROT_READ, MAP_SHARED, shmfd, 0); close(shmfd); - if (globals->shm_base == (uint32_t *)-1) globals->shm_base = NULL; - if (globals->shm_base == NULL) return -1; - - return 0; + return result; } +#endif /* TARGET_OS_SIMULATOR */ #ifdef NOTDEF static void @@ -215,102 +806,76 @@ shm_detach(void) } #endif -/* - * Initialization of global variables. Called once per process. - */ -void -_notify_init_globals(void * /* notify_globals_t */ _globals) -{ - notify_globals_t globals = _globals; - - pthread_mutex_init(&globals->notify_lock, NULL); - globals->token_id = INITIAL_TOKEN_ID; - globals->notify_common_token = -1; -} - -#if !_NOTIFY_HAS_ALLOC_ONCE -notify_globals_t -_notify_globals_impl(void) -{ - static dispatch_once_t once; - static notify_globals_t globals; - dispatch_once(&once, ^{ - globals = calloc(1, sizeof(struct notify_globals_s)); - _notify_init_globals(globals); - }); - return globals; -} -#endif - /* * _notify_lib_init is called for each new registration (event = EVENT_INIT). * It is also called to re-initialize when the library has detected that * notifyd has restarted (event = EVENT_REGEN). */ static uint32_t -_notify_lib_init(uint32_t event) +_notify_lib_init_locked(notify_globals_t globals, uint32_t event) { __block kern_return_t kstatus; - __block bool first = false; - int status, cid; - uint64_t state; - - notify_globals_t globals = _notify_globals(); + int status; /* notifyd sets NOTIFY_OPT_DISABLE to avoid re-entrancy issues */ - if (globals->client_opts & NOTIFY_OPT_DISABLE) return NOTIFY_STATUS_FAILED; + if (globals->client_opts & NOTIFY_OPT_DISABLE) return NOTIFY_STATUS_OPT_DISABLE; + /* We need to hold the global lock here otherwise "first" is racy */ + /* But then we don't need dispatch_once -- clean this up in 39829810 */ + os_unfair_lock_assert_owner(&globals->notify_lock); + /* Look up the notifyd server port just once. */ kstatus = KERN_SUCCESS; dispatch_once(&globals->notify_server_port_once, ^{ - first = true; kstatus = bootstrap_look_up2(bootstrap_port, NOTIFY_SERVICE_NAME, &globals->notify_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER); }); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; - - pthread_mutex_lock(&globals->notify_lock); + if ((kstatus != KERN_SUCCESS) || !(MACH_PORT_VALID(globals->notify_server_port))) + { + return NOTIFY_STATUS_SERVER_NOT_FOUND; + } /* * _dispatch_is_multithreaded() tells us if it is safe to use dispatch queues for - * a shared port for all registratios, and to watch for notifyd exiting / restarting. - * - * Note that _dispatch_is_multithreaded is weak imported, + * a shared port for all registrations (NOTIFY_OPT_DISPATCH), and to watch for notifyd + * exiting / restarting (NOTIFY_OPT_REGEN). */ - if (_dispatch_is_multithreaded) - { - if (_dispatch_is_multithreaded()) globals->client_opts |= (NOTIFY_OPT_DEMUX | NOTIFY_OPT_REGEN); + if (_dispatch_is_multithreaded()) { + globals->client_opts |= (NOTIFY_OPT_DISPATCH | NOTIFY_OPT_REGEN); } /* * Look up the server's PID and supported IPC version on the first call, * and on a regeneration event (when the server has restarted). */ - if (first || (event == EVENT_REGEN)) + pid_t last_pid = globals->notify_server_pid; + if ((last_pid == 0) || (event == EVENT_REGEN)) { - pid_t last_pid = globals->notify_server_pid; + uint32_t version, new_pid; - globals->notify_ipc_version = 0; globals->notify_server_pid = 0; - kstatus = _notify_server_register_plain(globals->notify_server_port, NOTIFY_IPC_VERSION_NAME, NOTIFY_IPC_VERSION_NAME_LEN, &cid, &status); - if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) + kstatus = _notify_server_checkin(globals->notify_server_port, &version, &new_pid, &status); + if (kstatus != KERN_SUCCESS) { - kstatus = _notify_server_get_state(globals->notify_server_port, cid, &state, &status); - if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) - { - globals->notify_ipc_version = state; - state >>= 32; - globals->notify_server_pid = state; - } + status = NOTIFY_STATUS_SERVER_CHECKIN_FAILED; + } + if (status != NOTIFY_STATUS_OK) + { + return status; + } - _notify_server_cancel(globals->notify_server_port, cid, &status); + /* + * protocol versions below 3 aren't supported anymore + */ + if (version < NOTIFY_IPC_VERSION_MIN_SUPPORTED) { + NOTIFY_INTERNAL_CRASH(version, "Unsupported protocol version"); + } + globals->notify_server_pid = new_pid; - if ((last_pid == globals->notify_server_pid) && (event == EVENT_REGEN)) - { - pthread_mutex_unlock(&globals->notify_lock); - return NOTIFY_STATUS_INVALID_REQUEST; - } + if ((new_pid == last_pid) && (event == EVENT_REGEN)) + { + return NOTIFY_STATUS_NO_REGEN_NEEDED; } if (globals->server_proc_source != NULL) @@ -321,11 +886,6 @@ _notify_lib_init(uint32_t event) } } - if (globals->notify_ipc_version < 2) - { - /* regen is not supported below version 2 */ - globals->client_opts &= ~NOTIFY_OPT_REGEN; - } /* * Create a source (DISPATCH_SOURCE_TYPE_PROC) to invoke _notify_lib_regenerate if notifyd restarts. @@ -334,47 +894,49 @@ _notify_lib_init(uint32_t event) if ((globals->server_proc_source == NULL) && (globals->client_opts & NOTIFY_OPT_REGEN) && (globals->notify_server_pid != 0)) { globals->server_proc_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)globals->notify_server_pid, DISPATCH_PROC_EXIT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); - dispatch_source_set_event_handler(globals->server_proc_source, ^{ _notify_lib_regenerate(1); }); + dispatch_source_set_event_handler_f(globals->server_proc_source, _notify_lib_server_restart_handler); dispatch_resume(globals->server_proc_source); } /* - * Create the shared multiplex ports if NOTIFY_OPT_DEMUX is set. + * Create the shared multiplex port if NOTIFY_OPT_DISPATCH is set. */ - if ((globals->client_opts & NOTIFY_OPT_DEMUX) && (globals->notify_common_port == MACH_PORT_NULL)) + if (((globals->client_opts & NOTIFY_OPT_DISPATCH) && (globals->notify_common_port == MACH_PORT_NULL)) || + ((globals->client_opts & NOTIFY_OPT_DISPATCH) && (globals->notify_common_port == MACH_PORT_DEAD))) { + mach_port_options_t opts = { + .flags = MPO_CONTEXT_AS_GUARD | MPO_STRICT | MPO_QLIMIT, + .mpl.mpl_qlimit = 16, + }; + mach_port_context_t guard = (uintptr_t)globals; kern_return_t kr; - task_t task = mach_task_self(); + mach_port_t mp; - kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &globals->notify_common_port); - if (kr == KERN_SUCCESS) - { - globals->notify_dispatch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, globals->notify_common_port, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); - dispatch_source_set_event_handler(globals->notify_dispatch_source, ^{ - notify_globals_t globals = _notify_globals(); - _notify_dispatch_handle(globals->notify_common_port); - }); - dispatch_source_set_cancel_handler(globals->notify_dispatch_source, ^{ - task_t task = mach_task_self(); - notify_globals_t globals = _notify_globals(); - mach_port_mod_refs(task, globals->notify_common_port, MACH_PORT_RIGHT_RECEIVE, -1); - }); - dispatch_resume(globals->notify_dispatch_source); + kr = mach_port_construct(mach_task_self(), &opts, guard, &mp); + if (kr != KERN_SUCCESS) { + NOTIFY_CLIENT_CRASH(kr, "mach_port_construct() failed: possible port leak"); } - } + globals->notify_common_port = mp; - pthread_mutex_unlock(&globals->notify_lock); - - if (globals->notify_common_port != MACH_PORT_NULL && (first || event == EVENT_REGEN)) - { - /* register the common port with notifyd */ - status = notify_register_mach_port(COMMON_PORT_KEY, &globals->notify_common_port, NOTIFY_REUSE, &globals->notify_common_token); + globals->notify_dispatch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); + dispatch_set_context(globals->notify_dispatch_source, globals); + dispatch_source_set_event_handler_f(globals->notify_dispatch_source, _notify_dispatch_handle); + dispatch_resume(globals->notify_dispatch_source); } return NOTIFY_STATUS_OK; } +static uint32_t +_notify_lib_init(notify_globals_t globals, uint32_t event) { + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); + uint32_t ret = _notify_lib_init_locked(globals, event); + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + return ret; +} + /* Reset all internal state at fork */ +/* Used in Libsystem without header declaration */ void _notify_fork_child(void) { @@ -389,229 +951,301 @@ _notify_fork_child(void) * * Also disable notify in the forked child of a multi-threaded parent that * used dispatch, since notify will use dispatch, and that will blow up. - * It's OK to make that check here by calling _dispatch_is_multithreaded(), - * since we will actually be looking at the parent's state. */ if (globals->notify_server_port != MACH_PORT_NULL) globals->client_opts = NOTIFY_OPT_DISABLE; - if (_dispatch_is_multithreaded) // weak imported symbol - { - if (_dispatch_is_multithreaded()) globals->client_opts = NOTIFY_OPT_DISABLE; - } + if (_dispatch_is_fork_of_multithreaded_parent()) globals->client_opts = NOTIFY_OPT_DISABLE; - globals->self_state = NULL; + _notify_lib_notify_state_init(&globals->self_state, NOTIFY_STATE_USE_LOCKS); globals->notify_server_port = MACH_PORT_NULL; - globals->notify_ipc_version = 0; globals->notify_server_pid = 0; - globals->token_table = NULL; - globals->token_name_table = NULL; - globals->fd_count = 0; globals->fd_clnt = NULL; globals->fd_srv = NULL; globals->fd_refcount = NULL; + globals->mp_size = 0; globals->mp_count = 0; globals->mp_list = NULL; - globals->mp_refcount = NULL; - globals->mp_mine = NULL; globals->shm_base = NULL; } +/* + * Create the registration structure associated with a client's token. + * Will create a name_node_t structure for the name if one does not exist. + * Note that this routine is NOT used to create the "base" for non-polled + * coalesced registrations. That's done by base_registration_create(). + */ static uint32_t -token_table_add(const char *name, size_t namelen, uint64_t nid, uint32_t token, uint32_t cid, uint32_t slot, uint32_t flags, int sig, int fd, mach_port_t mp, int lock) +client_registration_create_base( __attribute__((nonnull)) const char * name, uint64_t nid, uint32_t token, uint32_t cid, uint32_t slot, uint32_t flags, int sig, int fd, mach_port_t mp, bool create_base) { - token_table_node_t *t; - name_table_node_t *n; + name_node_t *name_node = NULL; + registration_node_t *reg_node; uint32_t warn_count = 0; notify_globals_t globals = _notify_globals(); - dispatch_once(&globals->token_table_once, ^{ - globals->token_table = _nc_table_new(CLIENT_TOKEN_TABLE_SIZE); - globals->token_name_table = _nc_table_new(CLIENT_TOKEN_TABLE_SIZE); - }); + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); - if (globals->token_table == NULL) return -1; - if (globals->token_name_table == NULL) return -1; - if (name == NULL) return -1; + /* should never happen, but check if the registration exists */ + reg_node = _nc_table_find_n(&globals->registration_table, token); + if (reg_node != NULL) goto client_registration_create_fail; - t = (token_table_node_t *)calloc(1, sizeof(token_table_node_t)); - if (t == NULL) return -1; + name_node = name_node_for_name_locked(globals, name, nid, true); + if (name_node == NULL) goto client_registration_create_fail; + mutex_lock(name_node->name, &name_node->lock, __func__, __LINE__); - t->refcount = 1; - - /* we will get t->name from the token_name_table */ - t->name = NULL; - - t->namelen = namelen; - t->token = token; - t->slot = slot; - t->val = 0; - t->flags = flags; - t->fd = fd; - t->mp = mp; - t->client_id = cid; - - if (lock != NO_LOCK) pthread_mutex_lock(&globals->notify_lock); - _nc_table_insert_n(globals->token_table, t->token, t); - - /* check if we have this name in the name table */ - n = _nc_table_find_get_key(globals->token_name_table, name, &(t->name)); - if (n == NULL) + reg_node = (registration_node_t *)calloc(1, sizeof(registration_node_t)); + if (reg_node == NULL) { - char *copy_name = strdup(name); - if (copy_name == NULL) - { - free(t); - if (lock != NO_LOCK) pthread_mutex_unlock(&globals->notify_lock); - return -1; - } - - t->name = (const char *)copy_name; - - /* create a new name table node */ - n = (name_table_node_t *)calloc(1, sizeof(name_table_node_t)); - if (n != NULL) - { - n->refcount = 1; - n->name_id = nid; - - /* the name table node "owns" the name */ - _nc_table_insert_pass(globals->token_name_table, copy_name, n); - t->name_node = n; - } - } - else - { - /* this token retains the name table node */ - t->name_node = n; - n->refcount++; - - if ((n->refcount % MULTIPLE_REGISTRATION_WARNING_TRIGGER) == 0) - { - warn_count = n->refcount; - } +#ifdef DEBUG + _notify_client_log(ASL_LEVEL_ERR, "client_registration_create name %s calloc failed errno %d [%s]\n", name, errno, strerror(errno)); +#endif + name_node_unlock_and_release(name_node); + goto client_registration_create_fail; } - if (lock != NO_LOCK) pthread_mutex_unlock(&globals->notify_lock); + reg_node->refcount = 1; + reg_node->token = token; + _nc_table_insert_n(&globals->registration_table, ®_node->token); - if (warn_count > 0) +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "client_registration_create token %u refcount %d -> %p", token, reg_node->refcount, reg_node); +#endif + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_REGISTRATION) { - char *msg; - asprintf(&msg, "notify name \"%s\" has been registered %d times - this may be a leak", name, warn_count); - if (msg) - _simple_asl_log(ASL_LEVEL_WARNING, "com.apple.notify", msg); - free(msg); + if (nid == NID_UNSET) _notify_client_log(ASL_LEVEL_NOTICE, "client_registration_create reg_node %p name %s name_node %p nid NID_UNSET token %u flags 0x%08x", reg_node, name, name_node, token, flags); + else if (nid == NID_CALLED_ONCE) _notify_client_log(ASL_LEVEL_NOTICE, "client_registration_create reg_node %p name %s name_node %p nid NID_CALLED_ONCE token %u flags 0x%08x", reg_node, name, name_node, token, flags); + else _notify_client_log(ASL_LEVEL_NOTICE, "client_registration_create reg_node %p name %s name_node %p nid %llu token %u flags 0x%08x", reg_node, name, name_node, nid, token, flags); + } +#endif + + /* no need to regenerate a coalesced registration */ + if (flags & NOTIFY_TYPE_COALESCED) flags &= ~NOTIFY_FLAG_REGEN; + + reg_node->flags = flags; + reg_node->slot = slot; + reg_node->val = 0; + reg_node->fd = fd; + reg_node->mp = mp; + reg_node->signal = sig; + reg_node->client_id = cid; + + /* + * Registration nodes retain their name node, so in theory we should + * retain name_node here. However, name_node_for_name() retains + * the name node, so we just make the assignment here and skip + * calling name_node_release() before returning from this routine. + */ + reg_node->name_node = name_node; + + /* + * If dispatch is available, add this registration to the name node's dispatch list. + * Doesn't apply to polled types (memory and plain). + */ + if ((globals->client_opts & NOTIFY_OPT_DISPATCH) && (flags & NOTIFY_TYPE_DELIVERED) && (flags & NOTIFY_TYPE_COALESCED)) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_REGISTRATION) _notify_client_log(ASL_LEVEL_NOTICE, "Add coalesced registration %p to name %s name node %p base %p", reg_node, name, name_node, name_node->coalesce_base); +#endif + name_node_add_coalesced_registration_locked(name_node, reg_node, create_base); } - return 0; + warn_count = name_node->refcount; + + if ((!name_node->has_been_warned) && (warn_count == MULTIPLE_REGISTRATION_WARNING_TRIGGER)) + { + name_node->has_been_warned = true; + REPORT_BAD_BEHAVIOR("notify name \"%s\" has been registered %d times - this may be a leak", name, warn_count); + } + + mutex_unlock(name_node->name, &name_node->lock, __func__, __LINE__); + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + return NOTIFY_STATUS_OK; + +client_registration_create_fail: + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + return NOTIFY_STATUS_CLIENT_REG_FAILED; } -static token_table_node_t * -token_table_find_retain(uint32_t token) +static uint32_t +client_registration_create(const char *name, uint64_t nid, uint32_t token, uint32_t cid, uint32_t slot, uint32_t flags, int sig, int fd, mach_port_t mp) { - token_table_node_t *t; - notify_globals_t globals = _notify_globals(); - - pthread_mutex_lock(&globals->notify_lock); - - t = (token_table_node_t *)_nc_table_find_n(globals->token_table, token); - if (t != NULL) t->refcount++; - - pthread_mutex_unlock(&globals->notify_lock); - - return t; + return client_registration_create_base(name, nid, token, cid, slot, flags, sig, fd, mp, false); } -static token_table_node_t * -token_table_find_no_lock(uint32_t token) +/* + * N.B. base_registration_create is called from notify_register_mach_port(), which holds the global lock + */ +static uint32_t +base_registration_create_locked(notify_globals_t globals, __attribute__((nonnull)) const char *name, uint64_t nid, uint32_t token, uint32_t flags, mach_port_t mp) { - notify_globals_t globals = _notify_globals(); - return (token_table_node_t *)_nc_table_find_n(globals->token_table, token); + os_unfair_lock_assert_owner(&globals->notify_lock); + + name_node_t *name_node = NULL; + registration_node_t *reg_node; + + /* should never happen, but check if the registration exists */ + reg_node = _nc_table_find_n(&globals->registration_table, token); + if (reg_node != NULL) return NOTIFY_STATUS_DOUBLE_REG; + + reg_node = (registration_node_t *)calloc(1, sizeof(registration_node_t)); + if (reg_node == NULL) + { +#ifdef DEBUG + _notify_client_log(ASL_LEVEL_ERR, "base_registration_create name %s calloc failed errno %d [%s]\n", name, errno, strerror(errno)); +#endif + return NOTIFY_STATUS_ALLOC_FAILED; + } + + name_node = name_node_for_name_locked(globals, name, nid, true); + if (name_node == NULL) + { + free(reg_node); + return NOTIFY_STATUS_NEW_NAME_FAILED; + } + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_REGISTRATION) + { + if (nid == NID_UNSET) _notify_client_log(ASL_LEVEL_NOTICE, "%s reg_node %p name %s name_node %p nid NID_UNSET token %u flags 0x%08x", __func__, reg_node, name, name_node, token, flags); + else if (nid == NID_CALLED_ONCE) _notify_client_log(ASL_LEVEL_NOTICE, "%s reg_node %p name %s name_node %p nid NID_CALLED_ONCE token %u flags 0x%08x", __func__, reg_node, name, name_node, token, flags); + else _notify_client_log(ASL_LEVEL_NOTICE, "%s reg_node %p name %s name_node %p nid %llu token %u flags 0x%08x", __func__, reg_node, name, name_node, nid, token, flags); + } +#endif + + reg_node->refcount = 1; + reg_node->token = token; + reg_node->client_id = token; + reg_node->flags = flags | NOTIFY_TYPE_COALESCE_BASE; + reg_node->mp = mp; + reg_node->val = 0; + reg_node->slot = SLOT_NONE; + reg_node->fd = FD_NONE; + reg_node->signal = SIGNAL_NONE; + reg_node->name_node = name_node; + + /* + * Registration nodes retain their name node, so in theory we should + * retain name_node here. However, name_node_for_name() retains + * the name node, so we just make the assignment here and skip + * calling name_node_release() before returning from this routine. + */ + name_node->coalesce_base = reg_node; + name_node->coalesce_base_token = token; + + _nc_table_insert_n(&globals->registration_table, ®_node->token); + return NOTIFY_STATUS_OK; } -static name_table_node_t * -name_table_find_retain_no_lock(const char *name) +static bool +check_name_access(char *name, uid_t uid) { - name_table_node_t *n; - notify_globals_t globals = _notify_globals(); + char str[64]; + size_t len; - n = (name_table_node_t *)_nc_table_find(globals->token_name_table, name); - if (n != NULL) n->refcount++; + /* root may do anything */ + if (uid == 0) return true; + + /* if name does not have "user.uid." as a prefix, it is not a user-protected namespace */ + if (strncmp(name, USER_PROTECTED_UID_PREFIX, USER_PROTECTED_UID_PREFIX_LEN)) + { + return true; + } + + snprintf(str, sizeof(str) - 1, "%s%d", USER_PROTECTED_UID_PREFIX, uid); + len = strlen(str); + + /* user may access user.uid. or a subtree name */ + return ((!strncmp(name, str, len)) && ((name[len] == '\0') || (name[len] == '.'))); - return n; } static void -name_table_release_no_lock(const char *name) -{ - name_table_node_t *n; - notify_globals_t globals = _notify_globals(); - - n = (name_table_node_t *)_nc_table_find(globals->token_name_table, name); - if (n != NULL) - { - if (n->refcount > 0) n->refcount--; - if (n->refcount == 0) - { - _nc_table_delete(globals->token_name_table, name); - free(n); - } - } -} - -static void -name_table_set_nid(const char *name, uint64_t nid) -{ - name_table_node_t *n; - notify_globals_t globals = _notify_globals(); - - pthread_mutex_lock(&globals->notify_lock); - - n = (name_table_node_t *)_nc_table_find(globals->token_name_table, name); - if (n != NULL) n->name_id = nid; - - pthread_mutex_unlock(&globals->notify_lock); -} - -static void -_notify_lib_regenerate_token(token_table_node_t *t) +_notify_lib_regenerate_registration(registration_node_t *r) { uint32_t type; - int status, new_slot; + int status; + int new_slot = SLOT_NONE; kern_return_t kstatus; mach_port_t port; - uint64_t new_nid; + uint64_t new_nid = NID_UNSET; size_t pathlen; - - if (t == NULL) return; - if (t->name == NULL) return; - if (t->flags & NOTIFY_FLAG_SELF) return; - if ((t->flags & NOTIFY_FLAG_REGEN) == 0) return; - if (!strcmp(t->name, COMMON_PORT_KEY)) return; + char *name = r->name_node->name; notify_globals_t globals = _notify_globals(); + if (r->flags & NOTIFY_FLAG_SELF) return; + if ((r->flags & NOTIFY_FLAG_REGEN) == 0) return; + port = MACH_PORT_NULL; - if (t->flags & NOTIFY_TYPE_PORT) + if (r->flags & NOTIFY_TYPE_PORT) { - port = globals->notify_common_port; + if(r->flags & NOTIFY_TYPE_COALESCED || r->flags & NOTIFY_TYPE_COALESCE_BASE) + { + port = globals->notify_common_port; + } + else + { + port = r->mp; + } + } + + if(!check_name_access(name, geteuid())) + { + REPORT_BAD_BEHAVIOR("BUG IN LIBNOTIFY CLIENT: registration held for restricted name %s with process uid %d", + name, geteuid()); } pathlen = 0; - if (t->path != NULL) pathlen = strlen(t->path); - type = t->flags & 0x000000ff; + if (r->path != NULL) pathlen = strlen(r->path) + 1; + type = r->flags & 0x000000ff; - kstatus = _notify_server_regenerate(globals->notify_server_port, (caddr_t)t->name, t->namelen, t->token, type, port, t->signal, t->slot, t->set_state_val, t->set_state_time, t->path, pathlen, t->path_flags, &new_slot, &new_nid, &status); + kstatus = _notify_server_regenerate(globals->notify_server_port, (caddr_t)name, r->token, type, port, r->signal, + r->slot, r->set_state_val, r->set_state_time, r->path, (mach_msg_type_number_t)pathlen, + r->path_flags, &new_slot, &new_nid, &status); - if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED; - if (status != NOTIFY_STATUS_OK) return; + assert(kstatus == KERN_SUCCESS); - t->slot = new_slot; + if(status != NOTIFY_STATUS_OK && status != NOTIFY_STATUS_DUP_CLIENT && + status != NOTIFY_STATUS_NO_REGEN_NEEDED) + { + REPORT_BAD_BEHAVIOR("Libnotify: _notify_server_regnerate failed for name %s", name); + } - /* reset the name_id in the name table node */ - if (t->name_node != NULL) t->name_node->name_id = new_nid; + + r->slot = new_slot; + r->name_node->name_id = new_nid; +} + +/* + * Invoked when server has died. + * Regenerates all registrations and state. + */ +static uint32_t +_notify_lib_regenerate_locked(notify_globals_t globals) +{ + uint32_t result; + + if ((globals->client_opts & NOTIFY_OPT_REGEN) == 0) return NOTIFY_STATUS_OK; + + /* if _notify_lib_init_lockec returns an error, regeneration is unnecessary */ + result = _notify_lib_init_locked(globals, EVENT_REGEN); + + if (result == NOTIFY_STATUS_NO_REGEN_NEEDED) { + return NOTIFY_STATUS_OK; + } + + if (result == NOTIFY_STATUS_OK) { + _nc_table_foreach_n(&globals->registration_table, ^bool(void *reg) { + _notify_lib_regenerate_registration(reg); + return true; + }); + } + + return result; } /* @@ -619,44 +1253,36 @@ _notify_lib_regenerate_token(token_table_node_t *t) * Regenerates all registrations and state. */ static void -_notify_lib_regenerate(int src) +_notify_lib_server_restart_handler(void *ctxt __unused) { - void *tt; - token_table_node_t *t; notify_globals_t globals = _notify_globals(); + + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); + (void)_notify_lib_regenerate_locked(globals); + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); - if ((globals->client_opts & NOTIFY_OPT_REGEN) == 0) return; - - /* _notify_lib_init returns an error if regeneration is unnecessary */ - if (_notify_lib_init(EVENT_REGEN) == NOTIFY_STATUS_OK) - { - pthread_mutex_lock(&globals->notify_lock); - - tt = _nc_table_traverse_start(globals->token_table); - while (tt != NULL) - { - t = _nc_table_traverse(globals->token_table, tt); - if (t == NULL) break; - _notify_lib_regenerate_token(t); - } - - _nc_table_traverse_end(globals->token_table, tt); - - pthread_mutex_unlock(&globals->notify_lock); - } + return; } /* - * Regenerate if the server PID (shared memory slot 0) has changed. + * Get the up to date shm_base if the server PID (shared memory slot 0) + * has changed. */ -static inline void -regenerate_check() +static inline uint32_t +regenerate_check(notify_globals_t globals) { - notify_globals_t globals = _notify_globals(); + uint32_t result = NOTIFY_STATUS_OK; - if ((globals->client_opts & NOTIFY_OPT_REGEN) == 0) return; + if ((globals->client_opts & NOTIFY_OPT_REGEN) == 0) return NOTIFY_STATUS_OK; - if ((globals->shm_base != NULL) && (globals->shm_base[0] != globals->notify_server_pid)) _notify_lib_regenerate(0); + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); + if ((globals->shm_base != NULL) && (globals->shm_base[0] != globals->notify_server_pid)) + { + result = _notify_lib_regenerate_locked(globals); + } + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + + return result; } /* notify_lock is required in notify_retain_file_descriptor */ @@ -669,7 +1295,7 @@ notify_retain_file_descriptor(int clnt, int srv) if (clnt < 0) return; if (srv < 0) return; - pthread_mutex_lock(&globals->notify_lock); + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); x = -1; for (i = 0; (i < globals->fd_count) && (x < 0); i++) @@ -680,31 +1306,28 @@ notify_retain_file_descriptor(int clnt, int srv) if (x >= 0) { globals->fd_refcount[x]++; - pthread_mutex_unlock(&globals->notify_lock); + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); return; } x = globals->fd_count; globals->fd_count++; - if (x == 0) - { - globals->fd_clnt = (int *)calloc(1, sizeof(int)); - globals->fd_srv = (int *)calloc(1, sizeof(int)); - globals->fd_refcount = (int *)calloc(1, sizeof(int)); - } - else - { - globals->fd_clnt = (int *)reallocf(globals->fd_clnt, globals->fd_count * sizeof(int)); - globals->fd_srv = (int *)reallocf(globals->fd_srv, globals->fd_count * sizeof(int)); - globals->fd_refcount = (int *)reallocf(globals->fd_refcount, globals->fd_count * sizeof(int)); - } + globals->fd_clnt = (int *)reallocf(globals->fd_clnt, globals->fd_count * sizeof(int)); + globals->fd_srv = (int *)reallocf(globals->fd_srv, globals->fd_count * sizeof(int)); + globals->fd_refcount = (int *)reallocf(globals->fd_refcount, globals->fd_count * sizeof(int)); if ((globals->fd_clnt == NULL) || (globals->fd_srv == NULL) || (globals->fd_refcount == NULL)) { +#ifdef DEBUG + _notify_client_log(ASL_LEVEL_ERR, "notify_retain_file_descriptor reallocf failed errno %d [%s]\n", errno, strerror(errno)); +#endif free(globals->fd_clnt); + globals->fd_clnt = NULL; free(globals->fd_srv); + globals->fd_srv = NULL; free(globals->fd_refcount); + globals->fd_refcount = NULL; globals->fd_count = 0; } else @@ -714,15 +1337,16 @@ notify_retain_file_descriptor(int clnt, int srv) globals->fd_refcount[x] = 1; } - pthread_mutex_unlock(&globals->notify_lock); + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); } -/* notify_lock is NOT required in notify_release_file_descriptor */ +// must be called with the global lock held static void -notify_release_file_descriptor(int fd) +notify_release_file_descriptor_locked(notify_globals_t globals, int fd) { + os_unfair_lock_assert_owner(&globals->notify_lock); + int x, i, j; - notify_globals_t globals = _notify_globals(); if (fd < 0) return; @@ -732,10 +1356,16 @@ notify_release_file_descriptor(int fd) if (globals->fd_clnt[i] == fd) x = i; } - if (x < 0) return; + if (x < 0) + { + return; + } if (globals->fd_refcount[x] > 0) globals->fd_refcount[x]--; - if (globals->fd_refcount[x] > 0) return; + if (globals->fd_refcount[x] > 0) + { + return; + } close(globals->fd_clnt[x]); close(globals->fd_srv[x]); @@ -743,8 +1373,11 @@ notify_release_file_descriptor(int fd) if (globals->fd_count == 1) { free(globals->fd_clnt); + globals->fd_clnt = NULL; free(globals->fd_srv); + globals->fd_srv = NULL; free(globals->fd_refcount); + globals->fd_refcount = NULL; globals->fd_count = 0; return; } @@ -764,185 +1397,126 @@ notify_release_file_descriptor(int fd) if ((globals->fd_clnt == NULL) || (globals->fd_srv == NULL) || (globals->fd_refcount == NULL)) { +#ifdef DEBUG + _notify_client_log(ASL_LEVEL_ERR, "notify_release_file_descriptor reallocf failed errno %d [%s]\n", errno, strerror(errno)); +#endif free(globals->fd_clnt); + globals->fd_clnt = NULL; free(globals->fd_srv); + globals->fd_srv = NULL; free(globals->fd_refcount); + globals->fd_refcount = NULL; globals->fd_count = 0; } + } /* notify_lock is required in notify_retain_mach_port */ static void -notify_retain_mach_port(mach_port_t mp, int mine) +notify_retain_mach_port(notify_globals_t globals, mach_port_t mp, int flags) { - int x, i; - notify_globals_t globals = _notify_globals(); - if (mp == MACH_PORT_NULL) return; + if (flags & _NOTIFY_COMMON_PORT) return; - pthread_mutex_lock(&globals->notify_lock); + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); - x = -1; - for (i = 0; (i < globals->mp_count) && (x < 0); i++) + struct mp_entry *entries = globals->mp_list; + + for (int i = 0; i < globals->mp_count; i++) { - if (globals->mp_list[i] == mp) x = i; + if (entries[i].mpl_port_name == mp) { + entries[i].mpl_refs++; + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + return; + } } - if (x >= 0) + int x = globals->mp_count; + globals->mp_count++; + + if (x >= globals->mp_size) { + if (globals->mp_size < 4) { + globals->mp_size = 4; + } else { + globals->mp_size *= 2; + } + entries = reallocf(entries, globals->mp_size * sizeof(struct mp_entry)); + globals->mp_list = entries; + if (os_unlikely(entries == NULL)) { + NOTIFY_CLIENT_CRASH(0, "Unable to allocate port array: " + "possible notification registration leak"); + } + } + + entries[x].mpl_port_name = mp; + entries[x].mpl_refs = 1; + entries[x].mpl_mine = ((flags & NOTIFY_REUSE) == 0); + + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); +} + +// must be called with the global lock held +static void +notify_release_mach_port_locked(notify_globals_t globals, mach_port_t mp, uint32_t flags) +{ + struct mp_entry *entries = globals->mp_list; + int x = -1; + + os_unfair_lock_assert_owner(&globals->notify_lock); + + if (mp == MACH_PORT_NULL) return; + if (mp == globals->notify_common_port) return; + + for (int i = 0; i < globals->mp_count; i++) + { + if (entries[i].mpl_port_name == mp) { + x = i; + break; + } + } + + if (x < 0) { - globals->mp_refcount[x]++; - pthread_mutex_unlock(&globals->notify_lock); return; } - x = globals->mp_count; - globals->mp_count++; - - if (x == 0) - { - globals->mp_list = (mach_port_t *)calloc(1, sizeof(mach_port_t)); - globals->mp_refcount = (int *)calloc(1, sizeof(int)); - globals->mp_mine = (int *)calloc(1, sizeof(int)); - } - else - { - globals->mp_list = (mach_port_t *)reallocf(globals->mp_list, globals->mp_count * sizeof(mach_port_t)); - globals->mp_refcount = (int *)reallocf(globals->mp_refcount, globals->mp_count * sizeof(int)); - globals->mp_mine = (int *)reallocf(globals->mp_mine, globals->mp_count * sizeof(int)); + if (entries[x].mpl_refs > 1) { + entries[x].mpl_refs--; + return; } - if ((globals->mp_list == NULL) || (globals->mp_refcount == NULL) || (globals->mp_mine == NULL)) + if (entries[x].mpl_mine) { - if (globals->mp_list != NULL) free(globals->mp_list); - if (globals->mp_refcount != NULL) free(globals->mp_refcount); - if (globals->mp_mine != NULL) free(globals->mp_mine); - globals->mp_count = 0; + int uref_mod = 0; + if (flags & NOTIFY_FLAG_RELEASE_SEND) uref_mod = -1; + mach_port_destruct(mach_task_self(), mp, uref_mod, 0); } - else - { - globals->mp_list[x] = mp; - globals->mp_refcount[x] = 1; - globals->mp_mine[x] = mine; - } - - pthread_mutex_unlock(&globals->notify_lock); -} - -/* notify_lock is NOT required in notify_release_mach_port */ -static void -notify_release_mach_port(mach_port_t mp, uint32_t flags) -{ - int x, i; - notify_globals_t globals = _notify_globals(); - - if (mp == MACH_PORT_NULL) return; - - x = -1; - for (i = 0; (i < globals->mp_count) && (x < 0); i++) - { - if (globals->mp_list[i] == mp) x = i; - } - - if (x < 0) return; - - if (globals->mp_refcount[x] > 0) globals->mp_refcount[x]--; - if (globals->mp_refcount[x] > 0) return; - - if (globals->mp_mine[x] == 1) - { - mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1); - - /* release send right if this is a self notification */ - if (flags & NOTIFY_FLAG_SELF) mach_port_deallocate(mach_task_self(), mp); - } - - if (flags & NOTIFY_FLAG_RELEASE_SEND) + else if (flags & NOTIFY_FLAG_RELEASE_SEND) { /* multiplexed registration holds a send right in Libnotify */ mach_port_deallocate(mach_task_self(), mp); } - if (globals->mp_count == 1) - { - if (globals->mp_list != NULL) free(globals->mp_list); - if (globals->mp_refcount != NULL) free(globals->mp_refcount); - if (globals->mp_mine != NULL) free(globals->mp_mine); - globals->mp_count = 0; - return; - } - - for (i = x + 1; i < globals->mp_count; i++) - { - globals->mp_list[i - 1] = globals->mp_list[i]; - globals->mp_refcount[i - 1] = globals->mp_refcount[i]; - globals->mp_mine[i - 1] = globals->mp_mine[i]; - } - + // swap removed element with the last one in the array globals->mp_count--; - - globals->mp_list = (mach_port_t *)reallocf(globals->mp_list, globals->mp_count * sizeof(mach_port_t)); - globals->mp_refcount = (int *)reallocf(globals->mp_refcount, globals->mp_count * sizeof(int)); - globals->mp_mine = (int *)reallocf(globals->mp_mine, globals->mp_count * sizeof(int)); - - if ((globals->mp_list == NULL) || (globals->mp_refcount == NULL) || (globals->mp_mine == NULL)) - { - if (globals->mp_list != NULL) free(globals->mp_list); - if (globals->mp_refcount != NULL) free(globals->mp_refcount); - if (globals->mp_mine != NULL) free(globals->mp_mine); - globals->mp_count = 0; - } -} - -static void -token_table_release_no_lock(token_table_node_t *t) -{ - notify_globals_t globals = _notify_globals(); - - if (t == NULL) return; - - if (t->refcount > 0) t->refcount--; - if (t->refcount > 0) return; - - notify_release_file_descriptor(t->fd); - notify_release_mach_port(t->mp, t->flags); - - if (t->block != NULL) - { - dispatch_async_f(t->queue, t->block, (dispatch_function_t)_Block_release); + if (x != globals->mp_count) { + entries[x] = entries[globals->mp_count]; } - t->block = NULL; - - if (t->queue != NULL) dispatch_release(t->queue); - t->queue = NULL; - - _nc_table_delete_n(globals->token_table, t->token); - name_table_release_no_lock(t->name); - - free(t->path); - free(t); -} - -static void -token_table_release(token_table_node_t *t) -{ - notify_globals_t globals = _notify_globals(); - - pthread_mutex_lock(&globals->notify_lock); - token_table_release_no_lock(t); - pthread_mutex_unlock(&globals->notify_lock); -} - -static notify_state_t * -_notify_lib_self_state() -{ - notify_globals_t globals = _notify_globals(); - - dispatch_once(&globals->self_state_once, ^{ - globals->self_state = _notify_lib_notify_state_new(NOTIFY_STATE_USE_LOCKS, 0); - }); - - return globals->self_state; + if (globals->mp_count == 0) + { + free(entries); + globals->mp_count = globals->mp_size = 0; + globals->mp_list = NULL; + } + else if (globals->mp_size > 4 && globals->mp_count <= globals->mp_size / 4) + { + entries = realloc(entries, sizeof(struct mp_entry) * globals->mp_size / 2); + if (entries) { + globals->mp_list = entries; + globals->mp_size /= 2; + } + } } /* SPI */ @@ -957,12 +1531,12 @@ notify_set_options(uint32_t opts) if ((opts & NOTIFY_OPT_ENABLE) == 0) return; /* re-enable by swapping in the saved server port and saved opts*/ - pthread_mutex_lock(&globals->notify_lock); + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); globals->client_opts = globals->saved_opts; globals->notify_server_port = globals->saved_server_port; - pthread_mutex_unlock(&globals->notify_lock); + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); return; } @@ -972,7 +1546,7 @@ notify_set_options(uint32_t opts) */ if (opts & NOTIFY_OPT_DISABLE) { - pthread_mutex_lock(&globals->notify_lock); + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); globals->saved_opts = globals->client_opts; globals->client_opts = NOTIFY_OPT_DISABLE; @@ -980,20 +1554,39 @@ notify_set_options(uint32_t opts) globals->saved_server_port = globals->notify_server_port; globals->notify_server_port = MACH_PORT_NULL; - pthread_mutex_unlock(&globals->notify_lock); + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); return; } globals->client_opts = opts; /* call _notify_lib_init to create ports / dispatch sources as required */ - _notify_lib_init(EVENT_INIT); + _notify_lib_init(globals, EVENT_INIT); +} + +/** + * Check if process has root access entitlement and should claim root access + * from notifyd. + */ +static bool +should_claim_root_access(void) +{ + static dispatch_once_t once; + static bool has_root_entitlement; + dispatch_once(&once, ^{ + xpc_object_t entitlement = xpc_copy_entitlement_for_token(ROOT_ENTITLEMENT_KEY, NULL); + if (entitlement == XPC_BOOL_TRUE) has_root_entitlement = true; + if (entitlement) xpc_release(entitlement); + }); + return has_root_entitlement; } /* * PUBLIC API */ + + /* * notify_post is a very simple API, but the implementation is * more complex to try to optimize the time it takes. @@ -1003,10 +1596,10 @@ notify_set_options(uint32_t opts) * _notify_server_post_4 (a MIG simpleroutine), the MIG call * allocates VM and copies the name string. It's much faster to * call using the ID number. The problem is mapping from name to - * ID number. The token table keeps track of all registered names + * ID number. The name table keeps track of all registered names * (in the client), but the registration calls are simpleroutines, * except for notify_register_check. notify_register_check saves - * the name ID in the token table, but the other routines set it + * the name ID in the name table, but the other routines set it * to NID_UNSET. * * In notify_post, we check if the name is known. If it is not, @@ -1024,9 +1617,9 @@ notify_set_options(uint32_t opts) * any addition cost. The ID number is reset to NID_CALLED_ONCE. * * If the client posts the same name again (the ID number is - * NID_CALLED_ONCE, we do a synchronous call to notifyd, sending + * NID_CALLED_ONCE) we do a synchronous call to notifyd, sending * the name string and getting back the name ID, whcih we save - * in the token table. This is simply a zero/one/many heuristic: + * in the name table. This is simply a zero/one/many heuristic: * If the client posts the same name more than once, we make the * guess that it's going to do it more frequently, and it's worth * the time it takes to fetch the ID from notifyd. @@ -1034,295 +1627,350 @@ notify_set_options(uint32_t opts) uint32_t notify_post(const char *name) { - notify_state_t *ns_self; + NOTIFY_POST(name); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + kern_return_t kstatus; uint32_t status; - size_t namelen = 0; - name_table_node_t *n; + name_node_t *n; uint64_t nid = UINT64_MAX; notify_globals_t globals = _notify_globals(); - regenerate_check(); + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; + + if (name == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_NAME; + } if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) { - ns_self = _notify_lib_self_state(); - if (ns_self == NULL) return NOTIFY_STATUS_FAILED; - _notify_lib_post(ns_self, name, 0, 0); + _notify_lib_post(&globals->self_state, name, 0, 0); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) return NOTIFY_STATUS_FAILED; + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } } - if (globals->notify_ipc_version == 0) - { - namelen = strlen(name); - kstatus = _notify_server_post(globals->notify_server_port, (caddr_t)name, namelen, (int32_t *)&status); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; - return status; - } - - namelen = strlen(name); - - /* Lock to prevent a race with notify cancel over the use of name IDs */ - pthread_mutex_lock(&globals->notify_lock); - /* See if we have a name ID for this name. */ - n = name_table_find_retain_no_lock(name); + n = name_node_for_name(name, NID_UNSET, false); if (n != NULL) { + mutex_lock(n->name, &n->lock, __func__, __LINE__); if (n->name_id == NID_UNSET) { /* First post goes using the name string. */ - kstatus = _notify_server_post_4(globals->notify_server_port, (caddr_t)name, namelen); + kstatus = _notify_server_post_4(globals->notify_server_port, (caddr_t)name, should_claim_root_access()); if (kstatus != KERN_SUCCESS) { - name_table_release_no_lock(name); - pthread_mutex_unlock(&globals->notify_lock); + name_node_unlock_and_release(n); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, + NOTIFY_STATUS_SERVER_POST_4_FAILED, __LINE__); return NOTIFY_STATUS_FAILED; } - n->name_id = NID_CALLED_ONCE; - name_table_release_no_lock(name); - pthread_mutex_unlock(&globals->notify_lock); - return NOTIFY_STATUS_OK; + name_node_set_nid_locked(n, NID_CALLED_ONCE); } else if (n->name_id == NID_CALLED_ONCE) { /* Post and fetch the name ID. Slow, but subsequent posts will be very fast. */ - kstatus = _notify_server_post_2(globals->notify_server_port, (caddr_t)name, namelen, &nid, (int32_t *)&status); + kstatus = _notify_server_post_2(globals->notify_server_port, (caddr_t)name, &nid, (int32_t *)&status, should_claim_root_access()); if (kstatus != KERN_SUCCESS) { - name_table_release_no_lock(name); - pthread_mutex_unlock(&globals->notify_lock); + name_node_unlock_and_release(n); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, + NOTIFY_STATUS_SERVER_POST_2_FAILED, __LINE__); return NOTIFY_STATUS_FAILED; } - if (status == NOTIFY_STATUS_OK) n->name_id = nid; - name_table_release_no_lock(name); - pthread_mutex_unlock(&globals->notify_lock); - return status; + name_node_set_nid_locked(n, nid); } else { /* We have the name ID. Do an async post using the name ID. Very fast. */ - kstatus = _notify_server_post_3(globals->notify_server_port, n->name_id); - name_table_release_no_lock(name); - pthread_mutex_unlock(&globals->notify_lock); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; - return NOTIFY_STATUS_OK; + kstatus = _notify_server_post_3(globals->notify_server_port, n->name_id, should_claim_root_access()); + if (kstatus != KERN_SUCCESS) + { + name_node_unlock_and_release(n); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, + NOTIFY_STATUS_SERVER_POST_3_FAILED, __LINE__); + return NOTIFY_STATUS_FAILED; + } } - } - pthread_mutex_unlock(&globals->notify_lock); + name_node_unlock_and_release(n); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_OK; + } /* Do an async post using the name string. Fast (but not as fast as using name ID). */ - kstatus = _notify_server_post_4(globals->notify_server_port, (caddr_t)name, namelen); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; + kstatus = _notify_server_post_4(globals->notify_server_port, (caddr_t)name, should_claim_root_access()); + if (kstatus != KERN_SUCCESS) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, + NOTIFY_STATUS_SERVER_POST_4_FAILED, __LINE__); + return NOTIFY_STATUS_FAILED; + } + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } -uint32_t -notify_set_owner(const char *name, uint32_t uid, uint32_t gid) + + +static void +_notify_dispatch_local_notification(registration_node_t *r) { - notify_state_t *ns_self; - kern_return_t kstatus; - uint32_t status; - notify_globals_t globals = _notify_globals(); + if (r == NULL) return; + if (r->queue == NULL) return; + if (r->block == NULL) return; - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - - if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NOTIFICATION) { - ns_self = _notify_lib_self_state(); - if (ns_self == NULL) return NOTIFY_STATUS_FAILED; - status = _notify_lib_set_owner(ns_self, name, uid, gid); - return status; + if (r->flags & NOTIFY_TYPE_COALESCED) _notify_client_log(ASL_LEVEL_NOTICE, "coalesced notification for token %d (name %s)", r->token, r->name_node->name); + else if (r->flags & NOTIFY_TYPE_COALESCE_BASE) _notify_client_log(ASL_LEVEL_NOTICE, "coalesced base notification for token %d (name %s)", r->token, r->name_node->name); + else _notify_client_log(ASL_LEVEL_NOTICE, "dispatch notification for token %d (name %s)", r->token, r->name_node->name); + } +#endif + + /* + * If the block calls notify_cancel, the node can get trashed, so + * we keep anything we need from the block (properly retained and released) + * in local variables. Concurrent notify_cancel() calls in the block are safe. + */ + int token = r->token; + notify_handler_t theblock = Block_copy(r->block); + dispatch_queue_t thequeue = r->queue; + dispatch_retain(thequeue); + + /* + * If DTrace probes are currently active that deal with the delivery of a + * notification, then the name needs to live until the end of the delivery. + * It is freed after delivery. + */ + char *name = NULL; + if ((NOTIFY_DELIVER_START_ENABLED() || NOTIFY_DELIVER_END_ENABLED()) && r && r->name_node && r->name_node->name) + { + name = strdup(r->name_node->name); } - if (globals->notify_server_port == MACH_PORT_NULL) - { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) return NOTIFY_STATUS_FAILED; - } + // notify_dispatch_source runs on a DISPATCH_QUEUE_PRIORITY_HIGH queue (IN + // QoS). Dispatching directly from that queue to the client queue taints the + // block with IN qos. Instead we create a detached wrapper and let the block + // run with the priority of the target queue hierachy. This is the same + // behaviour as with dispatch sources. + // + dispatch_block_t detached_block = dispatch_block_create(DISPATCH_BLOCK_DETACHED, ^{ + bool valid = notify_is_valid_token(token); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NOTIFICATION) _notify_client_log(ASL_LEVEL_NOTICE, "-> dispatch_async token %d (%svalid) registration node %p", token, valid ? "" : "in", r); +#endif + /* check if the token is still valid: it may have been cancelled */ + if (name) NOTIFY_DELIVER_START(name); + if (valid) theblock(token); + if (name) NOTIFY_DELIVER_END(name); + _Block_release(theblock); + dispatch_release(thequeue); - kstatus = _notify_server_set_owner(globals->notify_server_port, (caddr_t)name, strlen(name), uid, gid, (int32_t *)&status); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; - return status; -} + if (name) free(name); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NOTIFICATION) _notify_client_log(ASL_LEVEL_NOTICE, "<- dispatch_async token %d", token); +#endif + }); -uint32_t -notify_get_owner(const char *name, uint32_t *uid, uint32_t *gid) -{ - notify_state_t *ns_self; - kern_return_t kstatus; - uint32_t status; - notify_globals_t globals = _notify_globals(); - - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - - if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) - { - ns_self = _notify_lib_self_state(); - if (ns_self == NULL) return NOTIFY_STATUS_FAILED; - status = _notify_lib_get_owner(ns_self, name, uid, gid); - return status; - } - - if (globals->notify_server_port == MACH_PORT_NULL) - { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) return NOTIFY_STATUS_FAILED; - } - - kstatus = _notify_server_get_owner(globals->notify_server_port, (caddr_t)name, strlen(name), (int32_t *)uid, (int32_t *)gid, (int32_t *)&status); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; - return status; -} - -uint32_t -notify_set_access(const char *name, uint32_t access) -{ - notify_state_t *ns_self; - kern_return_t kstatus; - uint32_t status; - notify_globals_t globals = _notify_globals(); - - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - - if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) - { - ns_self = _notify_lib_self_state(); - if (ns_self == NULL) return NOTIFY_STATUS_FAILED; - status = _notify_lib_set_access(ns_self, name, access); - return status; - } - - if (globals->notify_server_port == MACH_PORT_NULL) - { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) return NOTIFY_STATUS_FAILED; - } - - kstatus = _notify_server_set_access(globals->notify_server_port, (caddr_t)name, strlen(name), access, (int32_t *)&status); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; - return status; -} - -uint32_t -notify_get_access(const char *name, uint32_t *access) -{ - notify_state_t *ns_self; - kern_return_t kstatus; - uint32_t status; - notify_globals_t globals = _notify_globals(); - - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - - if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) - { - ns_self = _notify_lib_self_state(); - if (ns_self == NULL) return NOTIFY_STATUS_FAILED; - status = _notify_lib_get_access(ns_self, name, access); - return status; - } - - if (globals->notify_server_port == MACH_PORT_NULL) - { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) return NOTIFY_STATUS_FAILED; - } - - kstatus = _notify_server_get_access(globals->notify_server_port, (caddr_t)name, strlen(name), (int32_t *)access, (int32_t *)&status); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; - return status; -} - -/* notifyd retains and releases a name when clients register and cancel. */ -uint32_t -notify_release_name(const char *name) -{ - return NOTIFY_STATUS_OK; + dispatch_async(thequeue, detached_block); + Block_release(detached_block); } static void -_notify_dispatch_handle(mach_port_t port) +_notify_dispatch_handle(void *context) { - token_table_node_t *t; + notify_globals_t globals = context; + mach_port_t port = globals->notify_common_port; + name_node_t *n; + registration_node_t *r; int token; mach_msg_empty_rcv_t msg; kern_return_t status; if (port == MACH_PORT_NULL) return; - memset(&msg, 0, sizeof(msg)); + for (int retries = 5; retries-- > 0; ) { + memset(&msg, 0, sizeof(msg)); - status = mach_msg(&msg.header, MACH_RCV_MSG, 0, sizeof(msg), port, 0, MACH_PORT_NULL); - if (status != KERN_SUCCESS) return; + /* + * The dispatch source watching our multiplexed mach port has fired. + * Read the message that is waiting and get the token ID from the mach header. + */ + status = mach_msg(&msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg), port, 0, MACH_PORT_NULL); + if (status != KERN_SUCCESS) return; - token = msg.header.msgh_id; + token = msg.header.msgh_id; - t = token_table_find_retain(token); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_NOTIFICATION) _notify_client_log(ASL_LEVEL_NOTICE, "_notify_dispatch_handle token %d", token); +#endif - if (t != NULL) - { - if ((t->queue != NULL) && (t->block != NULL)) + r = registration_node_find(token); + if (r == NULL) continue; + + n = r->name_node; + if (n == NULL) { - /* - * Don't reference into the token table node after token_table_release(). - * If the block calls notify_cancel, the node can get trashed, so - * we keep anything we need from the block (properly retained and released) - * in local variables. Concurrent notify_cancel() calls in the block are safe. - */ - notify_handler_t theblock = Block_copy(t->block); - dispatch_queue_t thequeue = t->queue; - dispatch_retain(thequeue); - - dispatch_async(thequeue, ^{ - token_table_node_t *t = token_table_find_no_lock(token); - if (t != NULL) theblock(token); - }); - - _Block_release(theblock); - dispatch_release(thequeue); + /* should not happen */ + registration_node_release(r); + continue; } - token_table_release(t); + mutex_lock(n->name, &n->lock, __func__, __LINE__); + + if (r->flags & NOTIFY_TYPE_COALESCE_BASE) + { + registration_node_t *x; + TAILQ_FOREACH(x, &n->coalesced, registration_coalesced_entry) + { + if (x != r) _notify_dispatch_local_notification(x); + } + } + else + { + _notify_dispatch_local_notification(r); + } + + mutex_unlock(n->name, &n->lock, __func__, __LINE__); + + registration_node_release(r); } } +#pragma mark - +#pragma mark registration + uint32_t notify_register_dispatch(const char *name, int *out_token, dispatch_queue_t queue, notify_handler_t handler) { - __block uint32_t status; - token_table_node_t *t; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + + uint32_t status; + registration_node_t *r; notify_globals_t globals = _notify_globals(); - regenerate_check(); + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } - if (queue == NULL) return NOTIFY_STATUS_FAILED; - if (handler == NULL) return NOTIFY_STATUS_FAILED; + if (queue == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_NULL_INPUT; + } - /* client is using dispatch: enable local demux and regeneration */ - notify_set_options(NOTIFY_OPT_DEMUX | NOTIFY_OPT_REGEN); + if (handler == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_NULL_INPUT; + } - status = notify_register_mach_port(name, &globals->notify_common_port, NOTIFY_REUSE, out_token); - if (status != NOTIFY_STATUS_OK) return status; + /* client is using dispatch: enable local demux / dispatch and regeneration */ + notify_set_options(NOTIFY_OPT_DISPATCH | NOTIFY_OPT_REGEN); - t = token_table_find_retain(*out_token); - if (t == NULL) return NOTIFY_STATUS_FAILED; + status = notify_register_mach_port(name, &globals->notify_common_port, NOTIFY_REUSE | _NOTIFY_COMMON_PORT, out_token); + if (status != NOTIFY_STATUS_OK) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } - t->queue = queue; - dispatch_retain(t->queue); - t->block = Block_copy(handler); - token_table_release(t); + r = registration_node_find(*out_token); + if (r == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, NOTIFY_STATUS_TOKEN_NOT_FOUND, __LINE__); + return NOTIFY_STATUS_FAILED; + } + r->queue = queue; + dispatch_retain(r->queue); + r->block = Block_copy(handler); + + registration_node_release(r); + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } @@ -1330,28 +1978,28 @@ notify_register_dispatch(const char *name, int *out_token, dispatch_queue_t queu static uint32_t notify_register_mux_fd(const char *name, int *out_token, int rfd, int wfd) { - __block uint32_t status; - token_table_node_t *t; + uint32_t status; + registration_node_t *r; int val; notify_globals_t globals = _notify_globals(); status = NOTIFY_STATUS_OK; - if (globals->notify_common_port == MACH_PORT_NULL) return NOTIFY_STATUS_FAILED; + if (globals->notify_common_port == MACH_PORT_NULL) return NOTIFY_STATUS_COMMON_PORT_NULL; - status = notify_register_mach_port(name, &globals->notify_common_port, NOTIFY_REUSE, out_token); + status = notify_register_mach_port(name, &globals->notify_common_port, NOTIFY_REUSE | _NOTIFY_COMMON_PORT, out_token); - t = token_table_find_retain(*out_token); - if (t == NULL) return NOTIFY_STATUS_FAILED; + r = registration_node_find(*out_token); + if (r == NULL) return NOTIFY_STATUS_TOKEN_NOT_FOUND; - t->token = *out_token; - t->fd = rfd; - t->queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); - dispatch_retain(t->queue); - val = htonl(t->token); - t->block = (notify_handler_t)Block_copy(^(int unused){ write(wfd, &val, sizeof(val)); }); + r->token = *out_token; + r->fd = rfd; + r->queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + dispatch_retain(r->queue); + val = htonl(r->token); + r->block = (notify_handler_t)Block_copy(^(int unused){ write(wfd, &val, sizeof(val)); }); - token_table_release(t); + registration_node_release(r); return NOTIFY_STATUS_OK; } @@ -1359,352 +2007,849 @@ notify_register_mux_fd(const char *name, int *out_token, int rfd, int wfd) uint32_t notify_register_check(const char *name, int *out_token) { - notify_state_t *ns_self; +#if TARGET_OS_SIMULATOR + return notify_register_plain(name, out_token); +#else +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + kern_return_t kstatus; uint32_t status, token; uint64_t nid; - int32_t slot, shmsize; - size_t namelen; + int slot; + int32_t shmsize; uint32_t cid; notify_globals_t globals = _notify_globals(); - regenerate_check(); + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - if (out_token == NULL) return NOTIFY_STATUS_FAILED; + if (name == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_NAME; + } + + if (out_token == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_NULL_INPUT; + } *out_token = -1; - namelen = strlen(name); if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) { - ns_self = _notify_lib_self_state(); - if (ns_self == NULL) return NOTIFY_STATUS_FAILED; - - token = OSAtomicIncrement32((int32_t *)&globals->token_id); - status = _notify_lib_register_plain(ns_self, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid); - if (status != NOTIFY_STATUS_OK) return status; + token = atomic_increment32(&globals->token_id); + status = _notify_lib_register_plain(&globals->self_state, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } cid = token; - token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0); + + status = client_registration_create(name, nid, token, cid, SLOT_NONE, + NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL); + + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } *out_token = token; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) return NOTIFY_STATUS_FAILED; + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } } - token = OSAtomicIncrement32((int32_t *)&globals->token_id); + token = atomic_increment32(&globals->token_id); kstatus = KERN_SUCCESS; + cid = token; + kstatus = _notify_server_register_check_2(globals->notify_server_port, (caddr_t)name, token, &shmsize, &slot, &nid, (int32_t *)&status); - if (globals->notify_ipc_version == 0) + if (kstatus != KERN_SUCCESS) { - nid = NID_UNSET; - kstatus = _notify_server_register_check(globals->notify_server_port, (caddr_t)name, namelen, &shmsize, &slot, (int32_t *)&cid, (int32_t *)&status); - } - else - { - cid = token; - kstatus = _notify_server_register_check_2(globals->notify_server_port, (caddr_t)name, namelen, token, &shmsize, &slot, &nid, (int32_t *)&status); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (kstatus: %d)", __func__, + NOTIFY_STATUS_REG_CHECK_2_FAILED, __LINE__, kstatus); + return NOTIFY_STATUS_FAILED; } - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; - if (status != NOTIFY_STATUS_OK) return status; + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } if (shmsize != -1) { + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); if (globals->shm_base == NULL) { - if (shm_attach(shmsize) != 0) return NOTIFY_STATUS_FAILED; - if (globals->shm_base == NULL) return NOTIFY_STATUS_FAILED; - } + if (!shm_attach(shmsize)) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + return NOTIFY_STATUS_FAILED; + } - token_table_add(name, namelen, nid, token, cid, slot, NOTIFY_TYPE_MEMORY | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0); + if (globals->shm_base == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, + NOTIFY_STATUS_SHM_BASE_REMAINS_NULL, __LINE__); + return NOTIFY_STATUS_FAILED; + } + } + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + + status = client_registration_create(name, nid, token, cid, slot, + NOTIFY_TYPE_MEMORY | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL); } else { - token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_TYPE_PLAIN | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0); + status = client_registration_create(name, nid, token, cid, SLOT_NONE, + NOTIFY_TYPE_PLAIN | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL); + } + + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } *out_token = token; - return status; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_OK; +#endif /* TARGET_OS_SIMULATOR */ } uint32_t notify_register_plain(const char *name, int *out_token) { - notify_state_t *ns_self; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + kern_return_t kstatus; uint32_t status; uint64_t nid; - size_t namelen; int token; uint32_t cid; notify_globals_t globals = _notify_globals(); - regenerate_check(); + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - - namelen = strlen(name); + if (name == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_NAME; + } if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) { - ns_self = _notify_lib_self_state(); - if (ns_self == NULL) return NOTIFY_STATUS_FAILED; - - token = OSAtomicIncrement32((int32_t *)&globals->token_id); - status = _notify_lib_register_plain(ns_self, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid); - if (status != NOTIFY_STATUS_OK) return status; + token = atomic_increment32(&globals->token_id); + status = _notify_lib_register_plain(&globals->self_state, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } cid = token; - token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0); + + status = client_registration_create(name, nid, token, cid, SLOT_NONE, + NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL); + + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, + "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } *out_token = token; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) return NOTIFY_STATUS_FAILED; + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } } - token = OSAtomicIncrement32((int32_t *)&globals->token_id); - - if (globals->notify_ipc_version == 0) + token = atomic_increment32(&globals->token_id); + cid = token; + kstatus = _notify_server_register_plain_2(globals->notify_server_port, (caddr_t)name, token); + if (kstatus != KERN_SUCCESS) { - kstatus = _notify_server_register_plain(globals->notify_server_port, (caddr_t)name, namelen, (int32_t *)&cid, (int32_t *)&status); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; - if (status != NOTIFY_STATUS_OK) return status; - } - else - { - cid = token; - kstatus = _notify_server_register_plain_2(globals->notify_server_port, (caddr_t)name, namelen, token); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, NOTIFY_STATUS_REG_PLAIN_2_FAILED, __LINE__); + return NOTIFY_STATUS_FAILED; } - token_table_add(name, namelen, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_PLAIN | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0); + + status = client_registration_create(name, NID_UNSET, token, cid, SLOT_NONE, + NOTIFY_TYPE_PLAIN | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL); + + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, + "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } *out_token = token; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } uint32_t notify_register_signal(const char *name, int sig, int *out_token) { - notify_state_t *ns_self; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + kern_return_t kstatus; uint32_t status; uint64_t nid; - size_t namelen; int token; uint32_t cid; notify_globals_t globals = _notify_globals(); - regenerate_check(); + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - - namelen = strlen(name); + if (name == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_NAME; + } if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) { - ns_self = _notify_lib_self_state(); - if (ns_self == NULL) return NOTIFY_STATUS_FAILED; - - token = OSAtomicIncrement32((int32_t *)&globals->token_id); - status = _notify_lib_register_signal(ns_self, name, NOTIFY_CLIENT_SELF, token, sig, 0, 0, &nid); - if (status != NOTIFY_STATUS_OK) return status; + token = atomic_increment32(&globals->token_id); + status = _notify_lib_register_signal(&globals->self_state, name, NOTIFY_CLIENT_SELF, token, sig, 0, 0, &nid); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } cid = token; - token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_SIGNAL, sig, FD_NONE, MACH_PORT_NULL, 0); + status = client_registration_create(name, nid, token, cid, SLOT_NONE, + NOTIFY_FLAG_SELF | NOTIFY_TYPE_SIGNAL, sig, FD_NONE, MACH_PORT_NULL); + + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, + "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } *out_token = token; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } - if (globals->client_opts & NOTIFY_OPT_DEMUX) + if (globals->client_opts & NOTIFY_OPT_DISPATCH) { - return notify_register_dispatch(name, out_token, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int unused){ kill(getpid(), sig); }); + status = notify_register_dispatch(name, out_token, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int unused){ kill(getpid(), sig); }); + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) return NOTIFY_STATUS_FAILED; + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } } - token = OSAtomicIncrement32((int32_t *)&globals->token_id); - - if (globals->notify_ipc_version == 0) + token = atomic_increment32(&globals->token_id); + cid = token; + kstatus = _notify_server_register_signal_2(globals->notify_server_port, (caddr_t)name, token, sig); + if (kstatus != KERN_SUCCESS) { - kstatus = _notify_server_register_signal(globals->notify_server_port, (caddr_t)name, namelen, sig, (int32_t *)&cid, (int32_t *)&status); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; - if (status != NOTIFY_STATUS_OK) return status; - } - else - { - cid = token; - kstatus = _notify_server_register_signal_2(globals->notify_server_port, (caddr_t)name, namelen, token, sig); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (kstatus: %d)", __func__, NOTIFY_STATUS_REG_SIGNAL_2_FAILED, __LINE__, kstatus); + return NOTIFY_STATUS_FAILED; } - token_table_add(name, namelen, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_SIGNAL | NOTIFY_FLAG_REGEN, sig, FD_NONE, MACH_PORT_NULL, 0); + status = client_registration_create(name, NID_UNSET, token, cid, SLOT_NONE, + NOTIFY_TYPE_SIGNAL | NOTIFY_FLAG_REGEN, sig, FD_NONE, MACH_PORT_NULL); + + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } *out_token = token; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } + +/* + * Port ownership rules: + * - notify common port is a pure receive right, with no send rights in our space + * + * - if the client doesn't pass NOTIFY_REUSE then we make a receive right that + * we track in the mach port references (notify_retain_mach_port, mpl_mine). + * + * - if we forward notifications to a local port, the library owns a send right + * tracked in the the registration with NOTIFY_FLAG_RELEASE_SEND + * + * - self notifications own a send right, tracked by the client_t structure + */ uint32_t notify_register_mach_port(const char *name, mach_port_name_t *notify_port, int flags, int *out_token) { - notify_state_t *ns_self; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + kern_return_t kstatus; uint32_t status; uint64_t nid; task_t task; int token, mine; - size_t namelen; uint32_t cid, tflags; - token_table_node_t *t; + registration_node_t *r; + name_node_t *n = NULL; mach_port_name_t port; notify_globals_t globals = _notify_globals(); + bool create_base = false; - regenerate_check(); + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - if (notify_port == NULL) return NOTIFY_STATUS_INVALID_PORT; + if (name == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_NAME; + } + + if (notify_port == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_PORT; + } mine = 0; - namelen = strlen(name); - task = mach_task_self(); if ((flags & NOTIFY_REUSE) == 0) { + /* caller wants a new port: create a new port with a receive right */ mine = 1; kstatus = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, notify_port); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; + if (kstatus != KERN_SUCCESS) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, + NOTIFY_STATUS_MACH_PORT_ALLOC_FAILED, __LINE__); + return NOTIFY_STATUS_FAILED; + } } - - kstatus = mach_port_insert_right(task, *notify_port, *notify_port, MACH_MSG_TYPE_MAKE_SEND); - if (kstatus != KERN_SUCCESS) + else if (!MACH_PORT_VALID(*notify_port)) { - if (mine == 1) mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1); - return NOTIFY_STATUS_FAILED; + return NOTIFY_STATUS_INVALID_PORT; } if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) { - ns_self = _notify_lib_self_state(); - if (ns_self == NULL) + /* we need to donate a Send right to the client */ + kstatus = mach_port_insert_right(mach_task_self(), *notify_port, *notify_port, MACH_MSG_TYPE_MAKE_SEND); + if (kstatus != KERN_SUCCESS) { - if (mine == 1) - { - mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1); - } - - mach_port_deallocate(task, *notify_port); - return NOTIFY_STATUS_FAILED; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_PORT; } - token = OSAtomicIncrement32((int32_t *)&globals->token_id); - status = _notify_lib_register_mach_port(ns_self, name, NOTIFY_CLIENT_SELF, token, *notify_port, 0, 0, &nid); + token = atomic_increment32(&globals->token_id); + status = _notify_lib_register_mach_port(&globals->self_state, name, NOTIFY_CLIENT_SELF, token, *notify_port, 0, 0, &nid); if (status != NOTIFY_STATUS_OK) { - if (mine == 1) + if (mine == 1) mach_port_destruct(mach_task_self(), *notify_port, -1, 0); + if (IS_INTERNAL_ERROR(status)) { - mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1); + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; } - - mach_port_deallocate(task, *notify_port); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } cid = token; - token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PORT, SIGNAL_NONE, FD_NONE, *notify_port, 0); + status = client_registration_create(name, nid, token, cid, SLOT_NONE, + NOTIFY_FLAG_SELF | NOTIFY_TYPE_PORT, SIGNAL_NONE, FD_NONE, *notify_port); + + if (status != NOTIFY_STATUS_OK) + { + if (mine == 1) mach_port_destruct(mach_task_self(), *notify_port, 0, 0); + _notify_lib_cancel(&globals->self_state, NOTIFY_CLIENT_SELF, token); + if (IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, + "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } *out_token = token; - notify_retain_mach_port(*notify_port, mine); + notify_retain_mach_port(globals, *notify_port, flags); + NOTIFY_REGISTER_MACH_PORT(name, *notify_port, flags, token); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } - if (globals->notify_server_port == MACH_PORT_NULL) + /* initialize if necessary */ + if ((globals->notify_server_port == MACH_PORT_NULL) || + ((globals->client_opts & NOTIFY_OPT_DISPATCH) && !MACH_PORT_VALID(globals->notify_common_port))) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) { - if (mine == 1) + /* error occurred: release receive right (if a new one was allocated) and the send right we added */ + if (mine == 1) mach_port_destruct(mach_task_self(), *notify_port, 0, 0); + if (IS_INTERNAL_ERROR(status)) { - mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1); + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; } - - mach_port_deallocate(task, *notify_port); - return NOTIFY_STATUS_FAILED; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } } - if ((globals->client_opts & NOTIFY_OPT_DEMUX) && (*notify_port != globals->notify_common_port)) + if (globals->client_opts & NOTIFY_OPT_DISPATCH) { + /* + * If it is a notify_register_mach_port(), we'll receive the + * notification on notify_common_port anyway, + * and forward it to the caller's port. + */ port = globals->notify_common_port; - kstatus = mach_port_insert_right(task, globals->notify_common_port, globals->notify_common_port, MACH_MSG_TYPE_MAKE_SEND); } else { + /* we'll register to receive the notification on the caller's port */ port = *notify_port; - kstatus = KERN_SUCCESS; } - if (kstatus == KERN_SUCCESS) - { - token = OSAtomicIncrement32((int32_t *)&globals->token_id); + tflags = NOTIFY_TYPE_PORT | NOTIFY_FLAG_REGEN; - if (globals->notify_ipc_version == 0) + token = -1; + cid = -1; + + if ((globals->client_opts & NOTIFY_OPT_DISPATCH) && ((flags & NOTIFY_NO_DISPATCH) == 0)) + { + /* + * Dance to protect n->coalesce_base. + * + * We take globals->notify_lock here. That prevents any other thread + * from looking up and/or creating a name node. We hold the lock + * until the name_node (n) is created if necessary, and n->coalesce_base + * has been set if it was NULL. The code below is the only code that + * assigns a value to n->coalesce_base, so even if some other thread has + * the name node, they won't clobber the value. + */ + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); + + n = _nc_table_find(&globals->name_node_table, name); + if (n == NULL) { - kstatus = _notify_server_register_mach_port(globals->notify_server_port, (caddr_t)name, namelen, port, token, (int32_t *)&cid, (int32_t *)&status); - if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_REGISTRATION) _notify_client_log(ASL_LEVEL_NOTICE, "%s [%d]: name table find %s -> NULL", __func__, __LINE__, name); +#endif + create_base = true; } + else if (n->coalesce_base == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_REGISTRATION) _notify_client_log(ASL_LEVEL_NOTICE, "%s [%d]: name table find %s -> %p, coalesce_base = NULL", __func__, __LINE__, name, n); +#endif + create_base = true; + } +#ifdef DEBUG else { - cid = token; - kstatus = _notify_server_register_mach_port_2(globals->notify_server_port, (caddr_t)name, namelen, token, port); + if (_libnotify_debug & DEBUG_REGISTRATION) _notify_client_log(ASL_LEVEL_NOTICE, "%s [%d]: name table find %s -> %p, coalesce_base = %p", __func__, __LINE__, name, n, n->coalesce_base); } - } +#endif - if (kstatus != KERN_SUCCESS) - { - if (mine == 1) + if (create_base) { - mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1); + /* base of coalesced registrations gets a private token */ + token = atomic_increment32(&globals->token_id); + cid = token; + + kstatus = _notify_server_register_mach_port_2(globals->notify_server_port, (caddr_t)name, token, globals->notify_common_port); + if (kstatus != KERN_SUCCESS) + { + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + if (mine == 1) mach_port_destruct(mach_task_self(), *notify_port, 0, 0); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (kstatus: %d)", __func__, + NOTIFY_STATUS_REG_MACH_PORT_2_FAILED, __LINE__, kstatus); + return NOTIFY_STATUS_FAILED; + } + + status = base_registration_create_locked(globals, name, NID_UNSET, token, tflags, + globals->notify_common_port); + if (status != NOTIFY_STATUS_OK) + { + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + if (mine == 1) mach_port_destruct(mach_task_self(), *notify_port, 0, 0); + if (IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + + return status; + } + } - mach_port_deallocate(task, *notify_port); - return NOTIFY_STATUS_FAILED; + /* we will need a pointer to the name node below - look it up while we still have the global lock */ + n = _nc_table_find(&globals->name_node_table, name); + + if(!create_base){ + registration_node_retain(n->coalesce_base); + } + + /* release globals->notify_lock here */ + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + + /* caller's token */ + token = atomic_increment32(&globals->token_id); + cid = token; + tflags |= NOTIFY_TYPE_COALESCED; + + /* + * If this call to notify_register_mach_port created the coalesce_base registration, + * we avoid an extra retain here. It is created with a refcount of 1, and it would get retained + * by the coalesced registration created in client_registration_create_base below. We + * want the refcount to be equal to the number of registrations in the coalesce list. + * We pass create_base down to client_registration_create_base to indicate that it should avoid + * the extra retain -- see name_node_add_coalesced_registration() + */ + status = client_registration_create_base(name, NID_UNSET, token, cid, SLOT_NONE, + tflags, SIGNAL_NONE, FD_NONE, *notify_port, create_base); + + if(!create_base){ + registration_node_release(n->coalesce_base); + } + + if(status != NOTIFY_STATUS_OK){ + if (mine == 1) mach_port_destruct(mach_task_self(), *notify_port, 0, 0); + if (IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } + + } + else + { + token = atomic_increment32(&globals->token_id); + cid = token; + + kstatus = _notify_server_register_mach_port_2(globals->notify_server_port, (caddr_t)name, token, port); + if (kstatus != KERN_SUCCESS) + { + if (mine == 1) mach_port_destruct(mach_task_self(), *notify_port, 0, 0); + mach_port_deallocate(task, *notify_port); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (kstatus: %d)", __func__, + NOTIFY_STATUS_REG_MACH_PORT_2_FAILED, __LINE__, kstatus); + return NOTIFY_STATUS_FAILED; + } + + status = client_registration_create_base(name, NID_UNSET, token, cid, SLOT_NONE, + tflags, SIGNAL_NONE, FD_NONE, *notify_port, false); + + if(status != NOTIFY_STATUS_OK) + { + if (mine == 1) mach_port_destruct(mach_task_self(), *notify_port, 0, 0); + if (IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } } - tflags = NOTIFY_TYPE_PORT; - if (port == globals->notify_common_port) tflags |= NOTIFY_FLAG_REGEN; - token_table_add(name, namelen, NID_UNSET, token, cid, SLOT_NONE, tflags, SIGNAL_NONE, FD_NONE, *notify_port, 0); - - if ((globals->client_opts & NOTIFY_OPT_DEMUX) && (*notify_port != globals->notify_common_port)) + if ((globals->client_opts & NOTIFY_OPT_DISPATCH) && (flags & _NOTIFY_COMMON_PORT) == 0) { - t = token_table_find_retain(token); - if (t == NULL) return NOTIFY_STATUS_FAILED; + /* + * If we use dispatch to forward notifications to this port, the the library needs the send right. + */ + kstatus = mach_port_insert_right(task, *notify_port, *notify_port, MACH_MSG_TYPE_MAKE_SEND); + if (kstatus != KERN_SUCCESS) + { + if (mine == 1) mach_port_destruct(mach_task_self(), *notify_port, 0, 0); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_PORT; + } + + r = registration_node_find(token); + if (r == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, + NOTIFY_STATUS_TOKEN_NOT_FOUND, __LINE__); + return NOTIFY_STATUS_FAILED; + } /* remember to release the send right when this gets cancelled */ - t->flags |= NOTIFY_FLAG_RELEASE_SEND; + r->flags |= NOTIFY_FLAG_RELEASE_SEND; port = *notify_port; - t->queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); - dispatch_retain(t->queue); - t->block = (notify_handler_t)Block_copy(^(int unused){ + r->queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + dispatch_retain(r->queue); + r->block = (notify_handler_t)Block_copy(^(int unused){ mach_msg_empty_send_t msg; kern_return_t kstatus; @@ -1716,73 +2861,105 @@ notify_register_mach_port(const char *name, mach_port_name_t *notify_port, int f msg.header.msgh_size = sizeof(mach_msg_empty_send_t); msg.header.msgh_id = token; - kstatus = mach_msg(&(msg.header), MACH_SEND_MSG | MACH_SEND_TIMEOUT, msg.header.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); + kstatus = mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, msg.header.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); + if (kstatus == MACH_SEND_TIMED_OUT) + { + dispatch_once(&globals->make_background_send_queue_once, ^{ + globals->background_send_queue = dispatch_queue_create("com.apple.notify.background.local.notification", NULL); + }); + + if (globals->background_send_queue != NULL) dispatch_async(globals->background_send_queue, ^{ + mach_msg_empty_send_t msg; + kern_return_t kstatus; + + /* send empty message to the port with msgh_id = token; */ + memset(&msg, 0, sizeof(msg)); + msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO); + msg.header.msgh_remote_port = port; + msg.header.msgh_local_port = MACH_PORT_NULL; + msg.header.msgh_size = sizeof(mach_msg_empty_send_t); + msg.header.msgh_id = token; + + kstatus = mach_msg(&msg.header, MACH_SEND_MSG, msg.header.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); + }); + } }); - token_table_release(t); + registration_node_release(r); } *out_token = token; - notify_retain_mach_port(*notify_port, mine); + notify_retain_mach_port(globals, *notify_port, flags); + NOTIFY_REGISTER_MACH_PORT(name, *notify_port, flags, token); - return NOTIFY_STATUS_OK; -} - -static char * -_notify_mk_tmp_path(int tid) -{ -#if TARGET_OS_EMBEDDED - int freetmp = 0; - char *path, *tmp = getenv("TMPDIR"); - - if (tmp == NULL) - { - asprintf(&tmp, "/tmp/com.apple.notify.%d", geteuid()); - mkdir(tmp, 0755); - freetmp = 1; - } - - if (tmp == NULL) return NULL; - - asprintf(&path, "%s/com.apple.notify.%d.%d", tmp, getpid(), tid); - if (freetmp) free(tmp); - return path; -#else - char tmp[PATH_MAX], *path; - - if (confstr(_CS_DARWIN_USER_TEMP_DIR, tmp, sizeof(tmp)) <= 0) return NULL; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); #endif - - path = NULL; - asprintf(&path, "%s/com.apple.notify.%d.%d", tmp, getpid(), tid); - return path; + return NOTIFY_STATUS_OK; } uint32_t notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int *out_token) { - notify_state_t *ns_self; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + uint32_t i, status; uint64_t nid; int token, mine, fdpair[2]; - size_t namelen; fileport_t fileport; kern_return_t kstatus; - uint32_t cid; + uint32_t cid = -1; notify_globals_t globals = _notify_globals(); - regenerate_check(); + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) + { + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } mine = 0; - if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; - if (notify_fd == NULL) return NOTIFY_STATUS_INVALID_FILE; + if (name == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor name=NULL\n"); + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_NAME; + } - namelen = strlen(name); + if (notify_fd == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor notify_fd=NULL\n"); + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_FILE; + } if ((flags & NOTIFY_REUSE) == 0) { - if (pipe(fdpair) < 0) return NOTIFY_STATUS_FAILED; + if (pipe(fdpair) < 0) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s [non-reused[ pipe failed errno=%d [%s]\n, name, errno, strerror(errno)"); + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, + NOTIFY_STATUS_PIPE_FAILED, __LINE__); + return NOTIFY_STATUS_FAILED; + } mine = 1; *notify_fd = fdpair[0]; @@ -1795,7 +2972,14 @@ notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int if (globals->fd_clnt[i] == *notify_fd) break; } - if (i >= globals->fd_count) return NOTIFY_STATUS_INVALID_FILE; + if (i >= globals->fd_count) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s [reused] file not found\n", name); + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_FILE; + } fdpair[0] = globals->fd_clnt[i]; fdpair[1] = globals->fd_srv[i]; @@ -1803,20 +2987,8 @@ notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) { - ns_self = _notify_lib_self_state(); - if (ns_self == NULL) - { - if (mine == 1) - { - close(fdpair[0]); - close(fdpair[1]); - } - - return NOTIFY_STATUS_FAILED; - } - - token = OSAtomicIncrement32((int32_t *)&globals->token_id); - status = _notify_lib_register_file_descriptor(ns_self, name, NOTIFY_CLIENT_SELF, token, fdpair[1], 0, 0, &nid); + token = atomic_increment32(&globals->token_id); + status = _notify_lib_register_file_descriptor(&globals->self_state, name, NOTIFY_CLIENT_SELF, token, fdpair[1], 0, 0, &nid); if (status != NOTIFY_STATUS_OK) { if (mine == 1) @@ -1825,19 +2997,46 @@ notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int close(fdpair[1]); } + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s [self] _notify_lib_register_file_descriptor failed status=%u\n", name, status); + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return status; } cid = token; - token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_FILE, SIGNAL_NONE, *notify_fd, MACH_PORT_NULL, 0); + status = client_registration_create(name, nid, token, cid, SLOT_NONE, + NOTIFY_FLAG_SELF | NOTIFY_TYPE_FILE, SIGNAL_NONE, *notify_fd, MACH_PORT_NULL); + + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, + "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } *out_token = token; notify_retain_file_descriptor(fdpair[0], fdpair[1]); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } - if (globals->client_opts & NOTIFY_OPT_DEMUX) + if (globals->client_opts & NOTIFY_OPT_DISPATCH) { /* * Use dispatch to do a write() on fdpair[1] when notified. @@ -1851,17 +3050,29 @@ notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int close(fdpair[1]); } + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s notify_register_mux_fd failed status=%u\n", name, status); + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return status; } notify_retain_file_descriptor(fdpair[0], fdpair[1]); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) { if (mine == 1) { @@ -1869,7 +3080,16 @@ notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int close(fdpair[1]); } - return NOTIFY_STATUS_FAILED; + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s _notify_lib_init failed status=%u\n", name, status); + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } } @@ -1883,21 +3103,17 @@ notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int close(fdpair[1]); } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s fileport_makeport failed errno=%d [%s]\n", name, errno, strerror(errno)); + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, + NOTIFY_STATUS_FILEPORT_MAKEPORT_FAILED, __LINE__); return NOTIFY_STATUS_FAILED; } - token = OSAtomicIncrement32((int32_t *)&globals->token_id); - - if (globals->notify_ipc_version == 0) - { - kstatus = _notify_server_register_file_descriptor(globals->notify_server_port, (caddr_t)name, namelen, (mach_port_t)fileport, token, (int32_t *)&cid, (int32_t *)&status); - if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; - } - else - { - kstatus = _notify_server_register_file_descriptor_2(globals->notify_server_port, (caddr_t)name, namelen, token, (mach_port_t)fileport); - } - + token = atomic_increment32(&globals->token_id); + kstatus = _notify_server_register_file_descriptor_2(globals->notify_server_port, (caddr_t)name, token, (mach_port_t)fileport); if (kstatus != KERN_SUCCESS) { if (mine == 1) @@ -1906,554 +3122,944 @@ notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int close(fdpair[1]); } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s _notify_server_register_file_descriptor[_2] failed status=0x%08xu\n", name, kstatus); + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (kstatus: %d)", __func__, + NOTIFY_STATUS_REG_FD_2_FAILED, __LINE__, kstatus); return NOTIFY_STATUS_FAILED; } - token_table_add(name, namelen, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_FILE, SIGNAL_NONE, *notify_fd, MACH_PORT_NULL, 0); + status = client_registration_create(name, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_FILE, + SIGNAL_NONE, *notify_fd, MACH_PORT_NULL); + + if (status != NOTIFY_STATUS_OK) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, + "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } *out_token = token; notify_retain_file_descriptor(fdpair[0], fdpair[1]); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } uint32_t notify_check(int token, int *check) { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + kern_return_t kstatus; - uint32_t status, val; - token_table_node_t *t; + uint32_t status = NOTIFY_STATUS_OK; + registration_node_t *r; uint32_t tid; notify_globals_t globals = _notify_globals(); - regenerate_check(); - - pthread_mutex_lock(&globals->notify_lock); - - t = token_table_find_no_lock(token); - if (t == NULL) + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) { - pthread_mutex_unlock(&globals->notify_lock); + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } + + if (check == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check check=NULL\n", token); + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_NULL_INPUT; + } + + r = registration_node_find(token); + if (r == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d registration node not found\n", token); + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_INVALID_TOKEN; } - if (t->flags & NOTIFY_FLAG_SELF) + if (r->flags & NOTIFY_FLAG_SELF) { - /* _notify_lib_check returns NOTIFY_STATUS_FAILED if self_state is NULL */ - status = _notify_lib_check(globals->self_state, NOTIFY_CLIENT_SELF, token, check); - pthread_mutex_unlock(&globals->notify_lock); - return status; + /* _notify_lib_check returns NOTIFY_STATUS_NULL_INPUT if self_state is NULL */ + status = _notify_lib_check(&globals->self_state, NOTIFY_CLIENT_SELF, token, check); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d self registration _notify_lib_check status %u check %d\n", token, status, *check); + if ((_libnotify_debug & DEBUG_USER) && (status != NOTIFY_STATUS_NULL_INPUT)) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d [self] _notify_lib_check failed status=%u\n", token, status); +#endif + goto release_and_return; } - if (t->flags & NOTIFY_TYPE_MEMORY) + if (r->flags & NOTIFY_TYPE_MEMORY) { if (globals->shm_base == NULL) { - pthread_mutex_unlock(&globals->notify_lock); - return NOTIFY_STATUS_FAILED; + status = NOTIFY_STATUS_SHM_BASE_NULL; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d [memory] globals->shm_base is NULL\n", token); +#endif + goto release_and_return; } *check = 0; - val = globals->shm_base[t->slot]; - if (t->val != val) + + mutex_lock("check_lock", &globals->check_lock, __func__, __LINE__); + if (r->val != globals->shm_base[r->slot]) { *check = 1; - t->val = val; + r->val = globals->shm_base[r->slot]; } + mutex_unlock("check_lock", &globals->check_lock, __func__, __LINE__); - pthread_mutex_unlock(&globals->notify_lock); - return NOTIFY_STATUS_OK; + + status = NOTIFY_STATUS_OK; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d shared memory check status %u check %d\n", token, status, *check); +#endif + goto release_and_return; } - tid = token; - if (globals->notify_ipc_version == 0) tid = t->client_id; - - pthread_mutex_unlock(&globals->notify_lock); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d in not a shared memory registration\n", token); +#endif if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) return NOTIFY_STATUS_FAILED; + status = _notify_lib_init(globals, EVENT_INIT); + if (status != 0) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d [non-memory] _notify_lib_init failed status=%u\n", token, status); +#endif + goto release_and_return; + } } - kstatus = _notify_server_check(globals->notify_server_port, tid, check, (int32_t *)&status); + tid = token; + if (r->flags & NOTIFY_TYPE_COALESCED) tid = r->name_node->coalesce_base_token; - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; + kstatus = _notify_server_check(globals->notify_server_port, tid, check, (int32_t *)&status); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d [%d] _notify_server_check kstatus 0x%08x status %u check %d\n", token, tid, kstatus, status, *check); +#endif + if (kstatus != KERN_SUCCESS) + { + status = NOTIFY_STATUS_SERVER_CHECK_FAILED; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d [non-memory] _notify_server_check failed kstatus=0x%08x status=%u\n", token, kstatus, status); +#endif + } + +release_and_return: + + NOTIFY_CHECK(token, *check); + + registration_node_release(r); + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d] status %u check %d\n", __func__, __LINE__ + 2, status, *check); +#endif return status; } +/* Used in Libinfo without header declaration */ uint32_t notify_peek(int token, uint32_t *val) { - token_table_node_t *t; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + + registration_node_t *r; uint32_t status; notify_globals_t globals = _notify_globals(); - regenerate_check(); - - t = token_table_find_retain(token); - if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; - - if (t->flags & NOTIFY_FLAG_SELF) + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) { - /* _notify_lib_peek returns NOTIFY_STATUS_FAILED if self_state is NULL */ - status = _notify_lib_peek(globals->self_state, NOTIFY_CLIENT_SELF, token, (int *)val); - token_table_release(t); + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return status; } - if (t->flags & NOTIFY_TYPE_MEMORY) + r = registration_node_find(token); + if (r == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_TOKEN; + } + + if (r->flags & NOTIFY_FLAG_SELF) + { + /* _notify_lib_peek returns NOTIFY_STATUS_NULL_INPUT if self_state is NULL */ + status = _notify_lib_peek(&globals->self_state, NOTIFY_CLIENT_SELF, token, (int *)val); + goto release_and_return; + } + + status = NOTIFY_STATUS_INVALID_REQUEST; + + if (r->flags & NOTIFY_TYPE_MEMORY) { if (globals->shm_base == NULL) { - token_table_release(t); - return NOTIFY_STATUS_FAILED; + status = NOTIFY_STATUS_SHM_BASE_NULL; + goto release_and_return; } - *val = globals->shm_base[t->slot]; - token_table_release(t); - return NOTIFY_STATUS_OK; + *val = globals->shm_base[r->slot]; + status = NOTIFY_STATUS_OK; } - token_table_release(t); - return NOTIFY_STATUS_INVALID_REQUEST; -} - -int * -notify_check_addr(int token) -{ - token_table_node_t *t; - uint32_t slot; - int *val; - notify_globals_t globals = _notify_globals(); - - regenerate_check(); - - t = token_table_find_retain(token); - if (t == NULL) return NULL; - - if (t->flags & NOTIFY_FLAG_SELF) - { - /* _notify_lib_check_addr returns NOTIFY_STATUS_FAILED if self_state is NULL */ - val = _notify_lib_check_addr(globals->self_state, NOTIFY_CLIENT_SELF, token); - token_table_release(t); - return val; - } - - if (t->flags & NOTIFY_TYPE_MEMORY) - { - slot = t->slot; - token_table_release(t); - - if (globals->shm_base == NULL) return NULL; - return (int *)&(globals->shm_base[slot]); - } - - token_table_release(t); - return NULL; +release_and_return: + + registration_node_release(r); + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } +/* Used by Libc, cctools, configd, kext_tools, Libnotify, and libresolv without header declaration */ uint32_t notify_monitor_file(int token, char *path, int flags) { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + kern_return_t kstatus; - uint32_t status, len; - token_table_node_t *t; + uint32_t status; + registration_node_t *r; char *dup; notify_globals_t globals = _notify_globals(); - regenerate_check(); - - if (path == NULL) return NOTIFY_STATUS_INVALID_REQUEST; - - t = token_table_find_retain(token); - if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; - - if (t->flags & NOTIFY_FLAG_SELF) + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) { - token_table_release(t); + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } + + if (path == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_REQUEST; + } + + r = registration_node_find(token); + if (r == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_TOKEN; + } + + if (r->flags & NOTIFY_FLAG_SELF) + { + registration_node_release(r); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_INVALID_REQUEST; } /* can only monitor one path with a token */ - if (t->path != NULL) + if (r->path != NULL) { - token_table_release(t); + registration_node_release(r); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_INVALID_REQUEST; } if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) { - token_table_release(t); - return NOTIFY_STATUS_FAILED; + registration_node_release(r); + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } } - len = strlen(path); dup = strdup(path); - if (dup == NULL) return NOTIFY_STATUS_FAILED; - - if (globals->notify_ipc_version == 0) + if (dup == NULL) { - kstatus = _notify_server_monitor_file(globals->notify_server_port, t->client_id, path, len, flags, (int32_t *)&status); - if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; - } - else - { - kstatus = _notify_server_monitor_file_2(globals->notify_server_port, token, path, len, flags); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, + NOTIFY_STATUS_STRDUP_FAILED, __LINE__); + return NOTIFY_STATUS_FAILED; } - t->path = dup; - t->path_flags = flags; + { + int xtoken = token; + int len = (uint32_t)(strlen(path) + 1); + if (r->flags & NOTIFY_TYPE_COALESCED) xtoken = r->name_node->coalesce_base_token; - token_table_release(t); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; + kstatus = _notify_server_monitor_file_2(globals->notify_server_port, xtoken, path, len, flags); + } + + r->path = dup; + r->path_flags = flags; + + registration_node_release(r); + + if (kstatus != KERN_SUCCESS) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (kstatus: %d)", __func__, + NOTIFY_STATUS_STRDUP_FAILED, __LINE__, kstatus); + return NOTIFY_STATUS_SERVER_MONITOR_FILE_2_FAILED; + } + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } +/* Used by kext_tools without header declaration */ uint32_t notify_get_event(int token, int *ev, char *buf, int *len) { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + + static bool report_deprecated_once = true; + + if(report_deprecated_once) + { + REPORT_BAD_BEHAVIOR("Libnotify client using deprecated function notify_get_event; this function does nothing"); + report_deprecated_once = false; + } + if (ev != NULL) *ev = 0; if (len != NULL) *len = 0; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } uint32_t notify_get_state(int token, uint64_t *state) { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + kern_return_t kstatus; uint32_t status; - token_table_node_t *t; + registration_node_t *r; uint64_t nid; notify_globals_t globals = _notify_globals(); - regenerate_check(); - - t = token_table_find_retain(token); - if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; - if (t->name_node == NULL) + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) { - token_table_release(t); + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } + + r = registration_node_find(token); + if (r == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_INVALID_TOKEN; } - if (t->flags & NOTIFY_FLAG_SELF) + if (r->name_node == NULL) { - /* _notify_lib_get_state returns NOTIFY_STATUS_FAILED if self_state is NULL */ - status = _notify_lib_get_state(globals->self_state, t->name_node->name_id, state, 0, 0); - token_table_release(t); + registration_node_release(r); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_TOKEN; + } + + if (r->flags & NOTIFY_FLAG_SELF) + { + /* _notify_lib_get_state returns NOTIFY_STATUS_NULL_INPUT if self_state is NULL */ + status = _notify_lib_get_state(&globals->self_state, r->name_node->name_id, state, 0, 0); + registration_node_release(r); + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return status; } if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) { - token_table_release(t); - return NOTIFY_STATUS_FAILED; + registration_node_release(r); + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } } - if (globals->notify_ipc_version == 0) { - kstatus = _notify_server_get_state(globals->notify_server_port, t->client_id, state, (int32_t *)&status); - if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; - } - else - { - if (t->name_node->name_id >= NID_CALLED_ONCE) + name_node_t *name_node = r->name_node; + int xtoken = token; + if (r->flags & NOTIFY_TYPE_COALESCED) xtoken = name_node->coalesce_base_token; + + // This is a race, but it is safe. The worst case is that nid is looked-up and set twice. + mutex_lock(name_node->name, &name_node->lock, __func__, __LINE__); + nid = name_node->name_id; + mutex_unlock(name_node->name, &name_node->lock, __func__, __LINE__); + + if ((nid == NID_UNSET) || (nid == NID_CALLED_ONCE)) { - kstatus = _notify_server_get_state_3(globals->notify_server_port, t->token, state, (uint64_t *)&nid, (int32_t *)&status); - if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) name_table_set_nid(t->name, nid); + kstatus = _notify_server_get_state_3(globals->notify_server_port, xtoken, state, (uint64_t *)&nid, (int32_t *)&status); + if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) name_node_set_nid(name_node, nid); } else { - kstatus = _notify_server_get_state_2(globals->notify_server_port, t->name_node->name_id, state, (int32_t *)&status); + kstatus = _notify_server_get_state_2(globals->notify_server_port, nid, state, (int32_t *)&status); } } - token_table_release(t); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; + registration_node_release(r); + + if (kstatus != KERN_SUCCESS) + { + status = NOTIFY_STATUS_SERVER_GET_STATE_2_FAILED; + } + + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return status; } uint32_t notify_set_state(int token, uint64_t state) { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + kern_return_t kstatus; uint32_t status; - token_table_node_t *t; + registration_node_t *r; uint64_t nid; notify_globals_t globals = _notify_globals(); - regenerate_check(); - - t = token_table_find_retain(token); - if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; - if (t->name_node == NULL) + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) { - token_table_release(t); + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } + + r = registration_node_find(token); + if (r == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_INVALID_TOKEN; } - if (t->flags & NOTIFY_FLAG_SELF) + if (r->name_node == NULL) { - /* _notify_lib_set_state returns NOTIFY_STATUS_FAILED if self_state is NULL */ - status = _notify_lib_set_state(globals->self_state, t->name_node->name_id, state, 0, 0); - token_table_release(t); + registration_node_release(r); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_TOKEN; + } + + if (r->flags & NOTIFY_FLAG_SELF) + { + /* _notify_lib_set_state returns NOTIFY_STATUS_NULL_INPUT if self_state is NULL */ + status = _notify_lib_set_state(&globals->self_state, r->name_node->name_id, state, 0, 0); + registration_node_release(r); + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return status; } if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) { - token_table_release(t); - return NOTIFY_STATUS_FAILED; + registration_node_release(r); + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } } status = NOTIFY_STATUS_OK; - if (globals->notify_ipc_version == 0) { - kstatus = _notify_server_set_state(globals->notify_server_port, t->client_id, state, (int32_t *)&status); - if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; - } - else - { - if (t->name_node->name_id >= NID_CALLED_ONCE) + name_node_t *name_node = r->name_node; + int xtoken = token; + if (r->flags & NOTIFY_TYPE_COALESCED) xtoken = name_node->coalesce_base_token; + + // This is a race, but it is safe. The worst case is that nid is looked-up and set twice. + mutex_lock(name_node->name, &name_node->lock, __func__, __LINE__); + nid = name_node->name_id; + mutex_unlock(name_node->name, &name_node->lock, __func__, __LINE__); + + if ((nid == NID_UNSET) || (nid == NID_CALLED_ONCE)) { - kstatus = _notify_server_set_state_3(globals->notify_server_port, t->token, state, (uint64_t *)&nid, (int32_t *)&status); - if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) name_table_set_nid(t->name, nid); + kstatus = _notify_server_set_state_3(globals->notify_server_port, xtoken, state, (uint64_t *)&nid, (int32_t *)&status, should_claim_root_access()); + if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) name_node_set_nid(name_node, nid); } else { status = NOTIFY_STATUS_OK; - kstatus = _notify_server_set_state_2(globals->notify_server_port, t->name_node->name_id, state); + kstatus = _notify_server_set_state_2(globals->notify_server_port, nid, state, should_claim_root_access()); } } if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) { - t->set_state_time = mach_absolute_time(); - t->set_state_val = state; + r->set_state_time = mach_absolute_time(); + r->set_state_val = state; } - token_table_release(t); - if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; + registration_node_release(r); + if (kstatus != KERN_SUCCESS) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, + NOTIFY_STATUS_SERVER_SET_STATE_2_FAILED, __LINE__); + return NOTIFY_STATUS_FAILED; + } + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } uint32_t notify_cancel(int token) { - token_table_node_t *t; - uint32_t status; - kern_return_t kstatus; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + + registration_node_t *r; notify_globals_t globals = _notify_globals(); + uint32_t status; - regenerate_check(); - - /* - * Lock to prevent a race with notify_post, which uses the name ID. - * If we are cancelling the last registration for this name, then we need - * to block those routines from getting the name ID from the name table. - * Once notifyd gets the cancellation, the name may vanish, and the name ID - * held in the name table would go stale. - * - * Uses token_table_find_no_lock() which does not retain, and - * token_table_release_no_lock() which releases the token. - */ - pthread_mutex_lock(&globals->notify_lock); - - t = token_table_find_no_lock(token); - if (t == NULL) + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) { - pthread_mutex_unlock(&globals->notify_lock); + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } + + mutex_lock("global", &globals->notify_lock, __func__, __LINE__); + + r = _nc_table_find_n(&globals->registration_table, token); + if (r == NULL) + { + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_INVALID_TOKEN; } - if (t->flags & NOTIFY_FLAG_SELF) - { - /* - * _notify_lib_cancel returns NOTIFY_STATUS_FAILED if self_state is NULL - * We let it fail quietly. - */ - _notify_lib_cancel(globals->self_state, NOTIFY_CLIENT_SELF, t->token); - - token_table_release_no_lock(t); - pthread_mutex_unlock(&globals->notify_lock); - return NOTIFY_STATUS_OK; + if (!(r->flags & NOTIFY_FLAG_CANCELED)) { + r->flags |= NOTIFY_FLAG_CANCELED; + registration_node_release_locked(globals, r); } - - if (globals->notify_ipc_version == 0) - { - kstatus = _notify_server_cancel(globals->notify_server_port, t->client_id, (int32_t *)&status); - if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; - } - else - { - kstatus = _notify_server_cancel_2(globals->notify_server_port, token); - } - - token_table_release_no_lock(t); - pthread_mutex_unlock(&globals->notify_lock); - - if ((kstatus == MIG_SERVER_DIED) || (kstatus == MACH_SEND_INVALID_DEST)) return NOTIFY_STATUS_OK; - else if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; - + mutex_unlock("global", &globals->notify_lock, __func__, __LINE__); + // Always return OK here as there's nothing a client can do if the server call failed return NOTIFY_STATUS_OK; } -bool -notify_is_valid_token(int val) -{ - token_table_node_t *t; - bool valid = false; - - if (val < 0) return false; - - notify_globals_t globals = _notify_globals(); - - pthread_mutex_lock(&globals->notify_lock); - - t = (token_table_node_t *)_nc_table_find_n(globals->token_table, val); - if (t != NULL) valid = true; - - pthread_mutex_unlock(&globals->notify_lock); - - return valid; -} - uint32_t notify_suspend(int token) { - token_table_node_t *t; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + + registration_node_t *r; uint32_t status, tid; kern_return_t kstatus; notify_globals_t globals = _notify_globals(); - regenerate_check(); - - t = token_table_find_retain(token); - if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; - - if (t->flags & NOTIFY_FLAG_SELF) + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) { - _notify_lib_suspend(globals->self_state, NOTIFY_CLIENT_SELF, t->token); - token_table_release(t); + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } + + r = registration_node_find(token); + if (r == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_TOKEN; + } + + if (r->flags & NOTIFY_FLAG_SELF) + { + _notify_lib_suspend(&globals->self_state, NOTIFY_CLIENT_SELF, r->token); + registration_node_release(r); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) { - token_table_release(t); - return NOTIFY_STATUS_FAILED; + registration_node_release(r); + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } } - tid = token; - if (globals->notify_ipc_version == 0) tid = t->client_id; + int xtoken = token; + if (r->flags & NOTIFY_TYPE_COALESCED) xtoken = r->name_node->coalesce_base_token; + + tid = xtoken; + if (r->flags & NOTIFY_TYPE_COALESCED) tid = r->name_node->coalesce_base_token; kstatus = _notify_server_suspend(globals->notify_server_port, tid, (int32_t *)&status); - token_table_release(t); - if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED; + registration_node_release(r); + + if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_SERVER_SUSPEND_FAILED; + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return status; } uint32_t notify_resume(int token) { - token_table_node_t *t; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + + registration_node_t *r; uint32_t status, tid; kern_return_t kstatus; notify_globals_t globals = _notify_globals(); - regenerate_check(); - - t = token_table_find_retain(token); - if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; - - if (t->flags & NOTIFY_FLAG_SELF) + status = regenerate_check(globals); + if (status != NOTIFY_STATUS_OK) { - _notify_lib_resume(globals->self_state, NOTIFY_CLIENT_SELF, t->token); - token_table_release(t); + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; + } + + r = registration_node_find(token); + if (r == NULL) + { +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return NOTIFY_STATUS_INVALID_TOKEN; + } + + if (r->flags & NOTIFY_FLAG_SELF) + { + _notify_lib_resume(&globals->self_state, NOTIFY_CLIENT_SELF, r->token); + registration_node_release(r); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return NOTIFY_STATUS_OK; } if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) { - token_table_release(t); - return NOTIFY_STATUS_FAILED; + registration_node_release(r); + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } } - tid = token; - if (globals->notify_ipc_version == 0) tid = t->client_id; + int xtoken = token; + if (r->flags & NOTIFY_TYPE_COALESCED) xtoken = r->name_node->coalesce_base_token; + + tid = xtoken; + if (r->flags & NOTIFY_TYPE_COALESCED) tid = r->name_node->coalesce_base_token; kstatus = _notify_server_resume(globals->notify_server_port, tid, (int32_t *)&status); - token_table_release(t); - if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED; + registration_node_release(r); + + if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_SERVER_RESUME_FAILED; + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return status; } uint32_t notify_suspend_pid(pid_t pid) { - uint32_t status; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + + uint32_t status = NOTIFY_STATUS_OK; kern_return_t kstatus; notify_globals_t globals = _notify_globals(); if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) { - return NOTIFY_STATUS_FAILED; + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } } - kstatus = _notify_server_suspend_pid(globals->notify_server_port, pid, (int32_t *)&status); + kstatus = _notify_server_suspend_pid(globals->notify_server_port, pid); - if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED; + if(kstatus != KERN_SUCCESS) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, kstatus, __LINE__); + status = NOTIFY_STATUS_FAILED; + } + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return status; } uint32_t notify_resume_pid(pid_t pid) { - uint32_t status; +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + + uint32_t status = NOTIFY_STATUS_OK; kern_return_t kstatus; notify_globals_t globals = _notify_globals(); if (globals->notify_server_port == MACH_PORT_NULL) { - status = _notify_lib_init(EVENT_INIT); - if (status != 0) + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) { - return NOTIFY_STATUS_FAILED; + + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } } - kstatus = _notify_server_resume_pid(globals->notify_server_port, pid, (int32_t *)&status); + kstatus = _notify_server_resume_pid(globals->notify_server_port, pid); - if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED; + if(kstatus != KERN_SUCCESS) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, kstatus, __LINE__); + status = NOTIFY_STATUS_FAILED; + } + +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif return status; } @@ -2461,5 +4067,82 @@ notify_resume_pid(pid_t pid) uint32_t notify_simple_post(const char *name) { - return notify_post(name); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__); +#endif + + static bool report_deprecated_once = true; + + if(report_deprecated_once) + { + REPORT_BAD_BEHAVIOR("Libnotify client using deprecated function notify_simple_post, use notify_post instead"); + report_deprecated_once = false; + } + + uint32_t status = notify_post(name); +#ifdef DEBUG + if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2); +#endif + return status; } + + +uint32_t +notify_dump_status(const char *filepath) +{ + notify_globals_t globals; + uint32_t status; + uint32_t kstatus; + int fileport_status; + int file_descriptor; + fileport_t fileport; + + globals = _notify_globals(); + + if(globals->notify_server_port == MACH_PORT_NULL) + { + status = _notify_lib_init(globals, EVENT_INIT); + if (status != NOTIFY_STATUS_OK) + { + if(IS_INTERNAL_ERROR(status)) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__); + status = NOTIFY_STATUS_FAILED; + } + + return status; + } + } + + + file_descriptor = creat(filepath, S_IWUSR); + if(file_descriptor < 0) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (errno: %d)", __func__, NOTIFY_STATUS_FAILED, __LINE__, errno); + return NOTIFY_STATUS_FAILED; + } + + + fileport = FILEPORT_NULL; + fileport_status = fileport_makeport(file_descriptor, &fileport); + if(fileport_status < 0) + { + close(file_descriptor); + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (fileport_status: %d)", __func__, NOTIFY_STATUS_FAILED, __LINE__, fileport_status); + return NOTIFY_STATUS_FAILED; + } + + + kstatus = _notify_server_dump(globals->notify_server_port, (mach_port_t)fileport); + close(file_descriptor); + if(kstatus != KERN_SUCCESS) + { + REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (kstatus: %d)", __func__, NOTIFY_STATUS_FAILED, __LINE__, kstatus); + return NOTIFY_STATUS_FAILED; + } + + + return NOTIFY_STATUS_OK; +} + + diff --git a/src/libnotify/notify_internal.h b/src/libnotify/notify_internal.h index a48d579d4..4d6d8e8bd 100644 --- a/src/libnotify/notify_internal.h +++ b/src/libnotify/notify_internal.h @@ -9,7 +9,7 @@ * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,83 +17,137 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ +#define __OS_EXPOSE_INTERNALS__ 1 +#include + #include #include -#include +#include +#include #include #include -struct notify_globals_s { - // Global lock. - pthread_mutex_t notify_lock; +#include "libnotify.h" - int32_t notify_ipc_version; +#define NOTIFY_INTERNAL_CRASH(c, x) __extension__({ \ + _os_set_crash_log_cause_and_message(c, "BUG IN LIBNOTIFY: " x); \ + __builtin_trap(); \ + }) + +#define NOTIFY_CLIENT_CRASH(c, x) __extension__({ \ + _os_set_crash_log_cause_and_message(c, \ + "BUG IN CLIENT OF LIBNOTIFY: " x); \ + __builtin_trap(); \ + }) + +#define NOTIFY_STATUS_SERVER_CHECKIN_FAILED 11 +// was NOTIFY_STATUS_LIB_SELF_STATE_FAILED 12 +#define NOTIFY_STATUS_SERVER_REGEN_FAILED 13 +#define NOTIFY_STATUS_CLIENT_REG_FAILED 14 +#define NOTIFY_STATUS_SERVER_POST_4_FAILED 15 +#define NOTIFY_STATUS_SERVER_POST_2_FAILED 16 +#define NOTIFY_STATUS_SERVER_POST_3_FAILED 17 +#define NOTIFY_STATUS_TOKEN_NOT_FOUND 18 +#define NOTIFY_STATUS_COMMON_PORT_NULL 19 +#define NOTIFY_STATUS_SERVER_PORT_NULL 20 +#define NOTIFY_STATUS_REG_CHECK_2_FAILED 21 +#define NOTIFY_STATUS_SHM_ATTACH_FAILED 22 +#define NOTIFY_STATUS_SHM_BASE_REMAINS_NULL 23 +#define NOTIFY_STATUS_REG_PLAIN_2_FAILED 24 +#define NOTIFY_STATUS_REG_SIGNAL_2_FAILED 25 +#define NOTIFY_STATUS_MACH_PORT_ALLOC_FAILED 26 +#define NOTIFY_STATUS_MACH_PORT_INSERT_RIGHT_FAILED 27 +#define NOTIFY_STATUS_REG_MACH_PORT_2_FAILED 28 +#define NOTIFY_STATUS_PIPE_FAILED 29 +#define NOTIFY_STATUS_FILEPORT_MAKEPORT_FAILED 30 +#define NOTIFY_STATUS_REG_FD_2_FAILED 31 +#define NOTIFY_STATUS_SHM_BASE_NULL 32 +#define NOTIFY_STATUS_SERVER_CHECK_FAILED 33 +#define NOTIFY_STATUS_STRDUP_FAILED 34 +#define NOTIFY_STATUS_SERVER_MONITOR_FILE_2_FAILED 35 +#define NOTIFY_STATUS_SERVER_GET_STATE_2_FAILED 36 +#define NOTIFY_STATUS_SERVER_SET_STATE_2_FAILED 37 +#define NOTIFY_STATUS_SERVER_SUSPEND_FAILED 38 +#define NOTIFY_STATUS_SERVER_RESUME_FAILED 39 +#define NOTIFY_STATUS_SERVER_SUSPEND_PID_FAILED 40 +#define NOTIFY_STATUS_SERVER_RESUME_PID_FAILED 41 +#define NOTIFY_STATUS_ALLOC_FAILED 42 +#define NOTIFY_STATUS_KILL_FAILED 43 +#define NOTIFY_STATUS_WRITE_FAILED 44 +#define NOTIFY_STATUS_MACH_MSG_TIMEOUT 45 +#define NOTIFY_STATUS_MACH_MSG_FAILED 46 +#define NOTIFY_STATUS_NEW_NAME_FAILED 47 +#define NOTIFY_STATUS_NEW_CLIENT_FAILED 48 +#define NOTIFY_STATUS_STATE_NULL 49 +#define NOTIFY_STATUS_CLIENT_NOT_FOUND 50 +#define NOTIFY_STATUS_DUP_CLIENT 51 +#define NOTIFY_STATUS_TYPE_ISSUE 52 +#define NOTIFY_STATUS_PATH_NODE_CREATE_FAILED 53 +#define NOTIFY_STATUS_INVALID_TIME_EVENT 54 +#define NOTIFY_STATUS_TIMER_FAILED 55 +#define NOTIFY_STATUS_DOUBLE_REG 56 +#define NOTIFY_STATUS_NO_REGEN_NEEDED 57 + +#define IS_INTERNAL_ERROR(X) (X >= 11) + +#define USER_PROTECTED_UID_PREFIX "user.uid." +#define USER_PROTECTED_UID_PREFIX_LEN 9 + +struct notify_globals_s +{ + /* global lock */ + os_unfair_lock notify_lock; + + /* notify_check() lock */ + os_unfair_lock check_lock; pid_t notify_server_pid; - uint32_t client_opts; + atomic_uint_fast32_t client_opts; uint32_t saved_opts; - // Last allocated name id. - uint64_t name_id; - - dispatch_once_t self_state_once; - notify_state_t *self_state; + notify_state_t self_state; dispatch_once_t notify_server_port_once; mach_port_t notify_server_port; mach_port_t saved_server_port; - + mach_port_t notify_common_port; int notify_common_token; dispatch_source_t notify_dispatch_source; dispatch_source_t server_proc_source; - - dispatch_once_t token_table_once; - table_t *token_table; - table_t *token_name_table; - uint32_t token_id; - - // File descriptor list. + + dispatch_once_t internal_once; + table_n_t registration_table; + table_t name_node_table; + atomic_uint_fast32_t token_id; + + dispatch_once_t make_background_send_queue_once; + dispatch_queue_t background_send_queue; + + /* file descriptor list */ uint32_t fd_count; int *fd_clnt; int *fd_srv; int *fd_refcount; - - // Mach port list. + + /* mach port list */ uint32_t mp_count; - mach_port_t *mp_list; - int *mp_refcount; - int *mp_mine; - - // Shared memory base address. + uint32_t mp_size; + struct mp_entry { + mach_port_t mpl_port_name; + int mpl_refs; + bool mpl_mine; + } *mp_list; + + /* shared memory base address */ uint32_t *shm_base; }; + typedef struct notify_globals_s *notify_globals_t; -#if __has_include() -#include -#if defined(OS_ALLOC_ONCE_KEY_LIBSYSTEM_NOTIFY) -#define _NOTIFY_HAS_ALLOC_ONCE 1 -#endif -#endif +__private_extern__ uint32_t _notify_lib_peek(notify_state_t *ns, pid_t pid, int token, int *val); -__attribute__((visibility("hidden"))) -void _notify_init_globals(void * /* notify_globals_t */ globals); - -__attribute__((visibility("hidden"))) -notify_globals_t _notify_globals_impl(void); - -__attribute__((__pure__)) -static inline notify_globals_t -_notify_globals(void) { -#if _NOTIFY_HAS_ALLOC_ONCE - return (notify_globals_t)os_alloc_once(OS_ALLOC_ONCE_KEY_LIBSYSTEM_NOTIFY, - sizeof(struct notify_globals_s), &_notify_init_globals); -#else - return _notify_globals_impl(); -#endif -} diff --git a/src/libnotify/notify_ipc.defs b/src/libnotify/notify_ipc.defs index 4dc42807b..1a732cb15 100644 --- a/src/libnotify/notify_ipc.defs +++ b/src/libnotify/notify_ipc.defs @@ -9,7 +9,7 @@ * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,136 +17,28 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ #include #include -subsystem notify_ipc 78945668; +subsystem notify_ipc 78945000; serverprefix _; import ; -import "notify_ipc_types.h"; - -type notify_name = ^ array [] of MACH_MSG_TYPE_BYTE +type notify_name = c_string[*:512] ctype : caddr_t; -routine _notify_server_post -( - server : mach_port_t; - name : notify_name; - out status : int; - ServerAuditToken audit : audit_token_t -); +type notify_path = array[] of char + ctype : caddr_t; -routine _notify_server_register_plain -( - server : mach_port_t; - name : notify_name; - out token : int; - out status : int; - ServerAuditToken audit : audit_token_t -); +UseSpecialReplyPort 1; -routine _notify_server_register_check -( - server : mach_port_t; - name : notify_name; - out size : int; - out slot : int; - out token : int; - out status : int; - ServerAuditToken audit : audit_token_t -); - -routine _notify_server_register_signal -( - server : mach_port_t; - name : notify_name; - sig: int; - out token : int; - out status : int; - ServerAuditToken audit : audit_token_t -); - -routine _notify_server_register_file_descriptor -( - server : mach_port_t; - name : notify_name; - fileport : mach_port_move_send_t; - ntoken : int; - out token : int; - out status : int; - ServerAuditToken audit : audit_token_t -); - -routine _notify_server_register_mach_port -( - server : mach_port_t; - name : notify_name; - port : mach_port_move_send_t; - ntoken : int; - out token : int; - out status : int; - ServerAuditToken audit : audit_token_t -); - -routine _notify_server_set_owner -( - server : mach_port_t; - name : notify_name; - user : int; - group : int; - out status : int; - ServerAuditToken audit : audit_token_t -); - -routine _notify_server_get_owner -( - server : mach_port_t; - name : notify_name; - out user : int; - out group : int; - out status : int; - ServerAuditToken audit : audit_token_t -); - -routine _notify_server_set_access -( - server : mach_port_t; - name : notify_name; - mode : int; - out status : int; - ServerAuditToken audit : audit_token_t -); - -routine _notify_server_get_access -( - server : mach_port_t; - name : notify_name; - out mode : int; - out status : int; - ServerAuditToken audit : audit_token_t -); - -routine _notify_server_release_name -( - server : mach_port_t; - name : notify_name; - out status : int; - ServerAuditToken audit : audit_token_t -); - -routine _notify_server_cancel -( - server : mach_port_t; - token : int; - out status : int; - ServerAuditToken audit : audit_token_t -); +skip; // was _notify_server_register_plain +skip; // was _notify_server_cancel routine _notify_server_check ( @@ -166,28 +58,6 @@ routine _notify_server_get_state ServerAuditToken audit : audit_token_t ); -routine _notify_server_set_state -( - server : mach_port_t; - token : int; - state : uint64_t; - out status : int; - ServerAuditToken audit : audit_token_t -); - -skip; /* formerly _notify_server_get_val */ - -skip; /* formerly _notify_server_set_val */ - -routine _notify_server_monitor_file -( - server : mach_port_t; - token : int; - path : notify_name; - flags : int; - out status : int; - ServerAuditToken audit : audit_token_t -); routine _notify_server_suspend ( @@ -205,30 +75,23 @@ routine _notify_server_resume ServerAuditToken audit : audit_token_t ); -routine _notify_server_suspend_pid +simpleroutine _notify_server_suspend_pid ( server : mach_port_t; pid : int; - out status : int; ServerAuditToken audit : audit_token_t ); -routine _notify_server_resume_pid +simpleroutine _notify_server_resume_pid ( server : mach_port_t; pid : int; - out status : int; ServerAuditToken audit : audit_token_t ); -simpleroutine _notify_server_simple_post -( - server : mach_port_t; - name : notify_name; - ServerAuditToken audit : audit_token_t -); -/* Additions for version 2 - more async support */ + +MsgOption MACH_SEND_PROPAGATE_QOS; routine _notify_server_post_2 ( @@ -236,6 +99,7 @@ routine _notify_server_post_2 name : notify_name; out name_id : uint64_t; out status : int; + claim_root_access : boolean_t; ServerAuditToken audit : audit_token_t ); @@ -243,6 +107,7 @@ simpleroutine _notify_server_post_3 ( server : mach_port_t; name_id : uint64_t; + claim_root_access : boolean_t; ServerAuditToken audit : audit_token_t ); @@ -250,9 +115,12 @@ simpleroutine _notify_server_post_4 ( server : mach_port_t; name : notify_name; + claim_root_access : boolean_t; ServerAuditToken audit : audit_token_t ); +MsgOption MACH_MSG_OPTION_NONE; + simpleroutine _notify_server_register_plain_2 ( server : mach_port_t; @@ -296,7 +164,7 @@ simpleroutine _notify_server_register_mach_port_2 server : mach_port_t; name : notify_name; token: int; - port : mach_port_move_send_t; + port : mach_port_make_send_t; ServerAuditToken audit : audit_token_t ); @@ -331,6 +199,7 @@ simpleroutine _notify_server_set_state_2 server : mach_port_t; name_id : uint64_t; state : uint64_t; + claim_root_access : boolean_t; ServerAuditToken audit : audit_token_t ); @@ -341,6 +210,7 @@ routine _notify_server_set_state_3 state : uint64_t; out nid : uint64_t; out status : int; + claim_root_access : boolean_t; ServerAuditToken audit : audit_token_t ); @@ -348,7 +218,7 @@ simpleroutine _notify_server_monitor_file_2 ( server : mach_port_t; token : int; - path : notify_name; + path : notify_path; flags : int; ServerAuditToken audit : audit_token_t ); @@ -364,10 +234,28 @@ routine _notify_server_regenerate prev_slot: int; prev_state : uint64_t; prev_time : uint64_t; - path : notify_name; + path : notify_path; path_flags: int; out new_slot : int; out new_name_id : uint64_t; out status : int; ServerAuditToken audit : audit_token_t ); + + +routine _notify_server_checkin +( + server : mach_port_t; + out version: uint32_t; + out server_pid : uint32_t; + out status : int; + ServerAuditToken audit : audit_token_t +); + + +routine _notify_server_dump +( + server : mach_port_t; + fileport : mach_port_move_send_t; + ServerAuditToken audit : audit_token_t +); diff --git a/src/libnotify/notify_ipc_types.h b/src/libnotify/notify_ipc_types.h deleted file mode 100644 index f1ae72270..000000000 --- a/src/libnotify/notify_ipc_types.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2003-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * Portions Copyright (c) 2003-2009 Apple Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef _NOTIFY_IPC_TYPES_H_ -#define _NOTIFY_IPC_TYPES_H_ - -#include -typedef char inline_data_t[2048]; - -#endif diff --git a/src/libnotify/notify_private.h b/src/libnotify/notify_private.h index aa475832d..7f1b4de32 100644 --- a/src/libnotify/notify_private.h +++ b/src/libnotify/notify_private.h @@ -25,23 +25,46 @@ #define __NOTIFY_PRIVATE_H__ #include +#include +#include #include -#define NOTIFY_OPT_DEMUX 0x00000001 -#define NOTIFY_OPT_REGEN 0x00000002 -#define NOTIFY_OPT_ENABLE 0x04000000 -#define NOTIFY_OPT_DISABLE 0x08000000 +#define NOTIFY_OPT_DISPATCH 0x00000001 +#define NOTIFY_OPT_REGEN 0x00000002 +#define NOTIFY_OPT_ENABLE 0x04000000 +#define NOTIFY_OPT_DISABLE 0x08000000 -uint32_t notify_suspend_pid(pid_t pid) +#define NOTIFY_NO_DISPATCH 0x80000000 + +#define ROOT_ENTITLEMENT_KEY "com.apple.notify.root_access" + +OS_EXPORT uint32_t notify_suspend_pid(pid_t pid) __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0); -uint32_t notify_resume_pid(pid_t pid) +OS_EXPORT uint32_t notify_resume_pid(pid_t pid) __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0); -uint32_t notify_simple_post(const char *name) -__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3); +OS_EXPORT uint32_t notify_simple_post(const char *name) +__API_DEPRECATED("No longer supported, use notify_post", macos(10.7, 10.15), ios(4.3, 13.0), watchos(1.0, 6.0), tvos(1.0, 13.0)); -void notify_set_options(uint32_t opts) +OS_EXPORT void notify_set_options(uint32_t opts) __OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_6_0); +OS_EXPORT void _notify_fork_child(void) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3); + +OS_EXPORT uint32_t notify_peek(int token, uint32_t *val) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3); + +OS_EXPORT uint32_t notify_monitor_file(int token, char *path, int flags) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3); + +OS_EXPORT uint32_t notify_get_event(int token, int *ev, char *buf, int *len) +__API_DEPRECATED("No longer supported", macos(10.7, 10.15), ios(4.3, 13.0), watchos(1.0, 6.0), tvos(1.0, 13.0)); + +OS_EXPORT uint32_t notify_register_plain(const char *name, int *out_token) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3); + +OS_EXPORT uint32_t notify_dump_status(const char *filepath); + #endif /* __NOTIFY_PRIVATE_H__ */ diff --git a/src/libnotify/notify_probes.d b/src/libnotify/notify_probes.d new file mode 100644 index 000000000..6669c1caf --- /dev/null +++ b/src/libnotify/notify_probes.d @@ -0,0 +1,7 @@ +provider notify { + probe register_mach_port(const char *name, mach_port_name_t notify_port, int flags, int token); + probe post(const char *name); + probe check(int token, int check); + probe deliver_start(const char *name); + probe deliver_end(const char *name); +}; diff --git a/src/libnotify/notifybench/notify_bench.c b/src/libnotify/notifybench/notify_bench.c new file mode 100644 index 000000000..a645f941b --- /dev/null +++ b/src/libnotify/notifybench/notify_bench.c @@ -0,0 +1,451 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "notify_private.h" + +#ifdef NO_OP_TESTS +extern uint32_t notify_no_op_str_sync(const char *name, size_t len); +extern uint32_t notify_no_op_str_async(const char *name, size_t len); +extern uint32_t notify_no_op_int_sync(int n); +extern uint32_t notify_no_op_int_async(int n); +#endif + +#define MAX_CNT 100 +#define MAX_SPL 10000 + +#define DEFAULT_CNT 50 +#if defined(__arm__) +#define DEFAULT_SPL 501 +#else +#define DEFAULT_SPL 1001 +#endif + +static uint32_t cnt = DEFAULT_CNT; +static uint32_t spl = DEFAULT_SPL; + +static long double time_round_to = (long double)200.0; + +static long double loop_cost; +static mach_timebase_info_data_t tbi; +static uint64_t dmy[MAX_SPL], reg_plain[MAX_SPL], cancel_plain[MAX_SPL], reg_port[MAX_SPL], cancel_port[MAX_SPL]; +static uint64_t post_plain1[MAX_SPL], post_plain2[MAX_SPL], post_plain3[MAX_SPL]; +static uint64_t set_state1[MAX_SPL], set_state2[MAX_SPL], get_state[MAX_SPL]; +static uint64_t reg_check[MAX_SPL], cancel_check[MAX_SPL]; +static uint64_t check1[MAX_SPL], check2[MAX_SPL], check3[MAX_SPL], check4[MAX_SPL], check5[MAX_SPL]; +static uint64_t reg_disp1[MAX_SPL], reg_disp2[MAX_SPL], cancel_disp[MAX_SPL]; + +volatile static int dispatch_changer = 0; + +#ifdef NO_OP_TESTS +static uint64_t nss[MAX_SPL], nsa[MAX_SPL], nis[MAX_SPL], nia[MAX_SPL]; +#endif + +static void __attribute__((noinline)) +print_result(uint64_t *s, const char *str) +{ + unsigned j; + uint64_t m; + long double dd = 0, mm, sd = 0; + + for (j = 0 ; j < spl; j++) { + dd += (long double)s[j]/(long double)cnt; + } + dd = dd/(long double)spl; + + for (j = 0 ; j < spl; j++) { + long double tmp = (long double)s[j]/(long double)cnt - dd; + sd += tmp * tmp; + } + sd = sqrtl(sd/(long double)spl); + + qsort_b(s, spl, sizeof(uint64_t), + ^(const void *a, const void *b){ + const uint64_t l = *(const uint64_t*)a; + const uint64_t r = *(const uint64_t*)b; + return l == r ? 0 : (l < r ? -1 : 1); + }); + m = s[spl/2]; + mm = (long double)m / (long double)cnt; + + if (tbi.numer != tbi.denom) { + dd *= tbi.numer; + dd /= tbi.denom; + mm *= tbi.numer; + mm /= tbi.denom; + sd *= tbi.numer; + sd /= tbi.denom; + } + + if (str) { + dd -= loop_cost; + mm -= loop_cost; + } else { + loop_cost = mm; + } + + dd /= NSEC_PER_USEC; + dd = roundl(dd * time_round_to) / time_round_to; + mm /= NSEC_PER_USEC; + mm = roundl(mm * time_round_to) / time_round_to; + sd /= NSEC_PER_USEC; + sd = roundl(sd * time_round_to) / time_round_to; + + if (!str) { + printf("%-40s %-8s %-8s %-8s\n", + "Symbol", "Median", "Average", "StdDev"); + } + printf("%-36s%8.3Lf us %8.3Lf us %8.3Lf us\n", + str ? str : "Empty loop:", mm, dd, sd); +} + +static void +notify_fence() +{ + int fence_token; + notify_register_check("com.apple.notify.test", &fence_token); + notify_cancel(fence_token); +} + +int +main(int argc, char *argv[]) +{ + uint32_t r; + kern_return_t kr; + unsigned i, j; + + kr = mach_timebase_info(&tbi); + assert(!kr); + + int tok; + r = notify_register_check("dummy.test", &tok); + assert(r == 0); + r = notify_cancel(tok); + assert(r == 0); + + int t[MAX_CNT]; + int t_2[MAX_CNT]; + mach_port_t p[MAX_CNT]; + char *n[MAX_CNT]; + size_t l[MAX_CNT]; + uint64_t s; + int check; + + volatile uint32_t spin = 0; + + dispatch_queue_t disp_q = dispatch_queue_create("Notify.Test", NULL); + + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-c")) cnt = atoi(argv[++i]); + else if (!strcmp(argv[i], "-s")) spl = atoi(argv[++i]) + 1; + } + + if (cnt > MAX_CNT) cnt = MAX_CNT; + if (spl > MAX_SPL) spl = MAX_SPL + 1; + + for (j = 0 ; j < spl; j++) + { + for (i = 0; i < cnt; i++) + { + r = asprintf(&n[i], "dummy.test.%d", i); + assert(r != -1); + l[i] = strlen(n[i]); + kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &p[i]); + assert(kr == 0); + } + + /* Empty Loop */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + spin++; + } + dmy[j] = mach_absolute_time() - s; + +#ifdef NO_OP_TESTS + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_no_op_str_sync(n[i], l[i]); + assert(r == 0); + } + nss[j] = mach_absolute_time() - s; + + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_no_op_str_async(n[i], l[i]); + assert(r == 0); + } + nsa[j] = mach_absolute_time() - s; + + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_no_op_int_sync(i); + assert(r == 0); + } + nis[j] = mach_absolute_time() - s; + + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_no_op_int_async(i); + assert(r == 0); + } + nia[j] = mach_absolute_time() - s; +#endif + + + + + + /* Register Plain */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_register_plain(n[i], &t[i]); + assert(r == 0); + } + reg_plain[j] = mach_absolute_time() - s; + + /* Post 1 */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_post(n[i]); + assert(r == 0); + } + post_plain1[j] = mach_absolute_time() - s; + + /* Post 2 */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_post(n[i]); + assert(r == 0); + } + post_plain2[j] = mach_absolute_time() - s; + + /* Post 3 */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_post(n[i]); + assert(r == 0); + } + post_plain3[j] = mach_absolute_time() - s; + + /* Cancel Plain */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_cancel(t[i]); + assert(r == 0); + } + cancel_plain[j] = mach_absolute_time() - s; + + /* Register Mach Port */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_register_mach_port(n[i], &p[i], NOTIFY_REUSE, &t[i]); + assert(r == 0); + } + reg_port[j] = mach_absolute_time() - s; + + + /* Set State 1 */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_set_state(t[i], 1); + assert(r == 0); + } + set_state1[j] = mach_absolute_time() - s; + + /* Get State */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + uint64_t dummy; + r = notify_get_state(t[i], &dummy); + assert(r == 0); + } + get_state[j] = mach_absolute_time() - s; + + /* Set State 2 */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_set_state(t[i], 2); + assert(r == 0); + } + set_state2[j] = mach_absolute_time() - s; + + /* Cancel Port */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_cancel(t[i]); + assert(r == 0); + } + cancel_port[j] = mach_absolute_time() - s; + + + + /* Register Check */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_register_check("com.apple.notify.test.check", &t[i]); + assert(r == 0); + } + reg_check[j] = mach_absolute_time() - s; + + /* Check 1 */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_check(t[i], &check); + assert(r == 0); + assert(check == 1); + } + check1[j] = mach_absolute_time() - s; + + /* Check 2 */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_check(t[i], &check); + assert(r == 0); + assert(check == 0); + } + check2[j] = mach_absolute_time() - s; + + /* Check 3 */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_check(t[i], &check); + assert(r == 0); + assert(check == 0); + } + check3[j] = mach_absolute_time() - s; + + notify_post("com.apple.notify.test.check"); + + notify_fence(); + + /* Check 4 */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_check(t[i], &check); + assert(r == 0); + assert(check == 1); + } + check4[j] = mach_absolute_time() - s; + + /* Check 5 */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_check(t[i], &check); + assert(r == 0); + assert(check == 0); + } + check5[j] = mach_absolute_time() - s; + + /* Cancel Check */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_cancel(t[i]); + assert(r == 0); + } + cancel_check[j] = mach_absolute_time() - s; + + /* Register Dispatch 1 */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_register_dispatch(n[i], &t[i], disp_q, ^(int x){ + dispatch_changer = x; + }); + assert(r == 0); + } + reg_disp1[j] = mach_absolute_time() - s; + + /* Register Dispatch 2 (Coalesced) */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_register_dispatch(n[i], &t_2[i], disp_q, ^(int x){ + dispatch_changer = x; + + }); + assert(r == 0); + } + reg_disp2[j] = mach_absolute_time() - s; + + /* Cancel Dispatch */ + s = mach_absolute_time(); + for (i = 0; i < cnt; i++) + { + r = notify_cancel(t[i]); + assert(r == 0); + r = notify_cancel(t_2[i]); + assert(r == 0); + } + cancel_disp[j] = mach_absolute_time() - s; + + for (i = 0; i < cnt; i++) + { + free(n[i]); + kr = mach_port_mod_refs(mach_task_self(), p[i], MACH_PORT_RIGHT_RECEIVE, -1); + assert(kr == 0); + } + + } + + print_result(dmy, NULL); +#ifdef NO_OP_TESTS + print_result(nss, "notify_no_op_str_sync:"); + print_result(nsa, "notify_no_op_str_async:"); + print_result(nis, "notify_no_op_int_sync:"); + print_result(nia, "notify_no_op_int_async:"); +#endif + + + print_result(reg_plain, "notify_register_plain:"); + print_result(post_plain1, "notify_post [plain 1]:"); + print_result(post_plain2, "notify_post [plain 2]:"); + print_result(post_plain3, "notify_post [plain 3]:"); + print_result(cancel_plain, "notify_cancel [plain]:"); + print_result(reg_port, "notify_register_mach_port:"); + print_result(set_state1, "notify_set_state [1]:"); + print_result(set_state2, "notify_set_state [2]:"); + print_result(get_state, "notify_get_state:"); + print_result(cancel_port, "notify_cancel [port]"); + + print_result(reg_check, "notify_register_check:"); + print_result(check1, "notify_check [1]:"); + print_result(check2, "notify_check [2]:"); + print_result(check3, "notify_check [3]:"); + print_result(check4, "notify_check [4]:"); + print_result(check5, "notify_check [5]:"); + print_result(cancel_check, "notfiy_cancel [check]:"); + + print_result(reg_disp1, "notify_register_dispatch [1]:"); + print_result(reg_disp2, "notify_register_dispatch [2]:"); + print_result(cancel_disp, "notify_cancel [both disp]:"); + + return 0; +} diff --git a/src/libnotify/notifyd/com.apple.notifyd.plist b/src/libnotify/notifyd/com.apple.notifyd.plist index 6750726f0..ac07b7337 100644 --- a/src/libnotify/notifyd/com.apple.notifyd.plist +++ b/src/libnotify/notifyd/com.apple.notifyd.plist @@ -2,6 +2,8 @@ + EnablePressuredExit + EnableTransactions Label diff --git a/src/libnotify/notifyd/com.apple.notifyd.sb b/src/libnotify/notifyd/com.apple.notifyd.sb new file mode 100644 index 000000000..4aa3173bb --- /dev/null +++ b/src/libnotify/notifyd/com.apple.notifyd.sb @@ -0,0 +1,24 @@ +;; Copyright (c) 2015 Apple Inc. All Rights reserved. +;; +;; WARNING: The sandbox rules in this file currently constitute +;; Apple System Private Interface and are subject to change at any time and +;; without notice. +;; + +(version 1) +(deny default) +(import "system.sb") + +;; Allow files to be read +(allow file-read*) + +;; Allow debug status files to be written +(allow file-write* + (regex #"^/private/var/run/notifyd") +) + +;; Allow UNIX signals +(allow signal) + +;; Allow shared memory +(allow ipc-posix-shm) diff --git a/src/libnotify/notifyd/notify.conf.MacOSX b/src/libnotify/notifyd/notify.conf.MacOSX index fe9b78ac9..9311a7d7c 100644 --- a/src/libnotify/notifyd/notify.conf.MacOSX +++ b/src/libnotify/notifyd/notify.conf.MacOSX @@ -3,6 +3,7 @@ # reserve com.apple.system. 0 0 rwr-r- +reserve com.apple.system.clock_set 0 266 rwrwr- monitor com.apple.system.timezone /etc/localtime monitor com.apple.system.info:/etc/hosts /etc/hosts monitor com.apple.system.info:/etc/services /etc/services diff --git a/src/libnotify/notifyd/notify.conf.iOSSimulator b/src/libnotify/notifyd/notify.conf.iOSSimulator new file mode 100644 index 000000000..9070e3a3b --- /dev/null +++ b/src/libnotify/notifyd/notify.conf.iOSSimulator @@ -0,0 +1,5 @@ +# +# Notification Center configuration file +# + +monitor com.apple.system.timezone /etc/localtime diff --git a/src/libnotify/notifyd/notify.conf.iPhone b/src/libnotify/notifyd/notify.conf.iPhone index 0303cb75b..c40e4e281 100644 --- a/src/libnotify/notifyd/notify.conf.iPhone +++ b/src/libnotify/notifyd/notify.conf.iPhone @@ -3,5 +3,7 @@ # reserve com.apple.system. 0 0 rwr-r- -reserve com.apple.system.clock_set 0 501 rwrwr- +reserve com.apple.system.clock_set 0 266 rwrwr- monitor com.apple.system.timezone /var/db/timezone/localtime +set com.apple.system.batterysavermode +set com.apple.system.batterysavermode.discretionary diff --git a/src/libnotify/notifyd/notify_ipc.defs b/src/libnotify/notifyd/notify_ipc.defs deleted file mode 120000 index 51c2d6a8f..000000000 --- a/src/libnotify/notifyd/notify_ipc.defs +++ /dev/null @@ -1 +0,0 @@ -../notify_ipc.defs \ No newline at end of file diff --git a/src/libnotify/notifyd/notify_proc.c b/src/libnotify/notifyd/notify_proc.c index 79344b211..82121133d 100644 --- a/src/libnotify/notifyd/notify_proc.c +++ b/src/libnotify/notifyd/notify_proc.c @@ -21,6 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include "notifyd.h" #include #include #include @@ -33,261 +34,322 @@ #include #include #include +#include #include "notify.h" -#include "notifyd.h" #include "service.h" #include "notify_ipc.h" #include "notify_ipcServer.h" +#include "notify_internal.h" +#include "notifyServer.h" +#include static void -cancel_subscription(client_t *c) +port_arm_mach_notifications(mach_port_t port) +{ + kern_return_t kr; + mach_port_t previous; + + /* arm both DEAD_NAME & SEND_POSSIBLE */ + kr = mach_port_request_notification(mach_task_self(), port, + MACH_NOTIFY_SEND_POSSIBLE, 0, global.mach_notify_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); + if (kr != KERN_SUCCESS) { + NOTIFY_INTERNAL_CRASH(kr, "Unable to arm send-possible"); + } + if (previous != MACH_PORT_NULL) { + NOTIFY_INTERNAL_CRASH(kr, "send-possible already armed??"); + } +} + +static void +port_free(void *_pp) +{ + notify_state_t *ns = &global.notify_state; + port_data_t *pdata = _pp; + + if (!LIST_EMPTY(&pdata->clients)) { + NOTIFY_INTERNAL_CRASH(0, "port_proc still had clients"); + } + + _nc_table_delete_n(&ns->port_table, pdata->port); + mach_port_deallocate(mach_task_self(), pdata->port); + + ns->stat_portproc_free++; + free(pdata); +} + +static void +proc_free(void *_pp) +{ + notify_state_t *ns = &global.notify_state; + proc_data_t *pdata = _pp; + + if (!LIST_EMPTY(&pdata->clients)) { + NOTIFY_INTERNAL_CRASH(0, "port_proc still had clients"); + } + + _nc_table_delete_n(&ns->proc_table, pdata->pid); + + ns->stat_portproc_free++; + + dispatch_release(pdata->src); + free(pdata); +} + +static void +proc_create(notify_state_t *ns, client_t *c, pid_t proc, dispatch_source_t src) +{ + proc_data_t *pdata = malloc(sizeof(proc_data_t)); + + if (pdata == NULL) { + // failing here is not an option, and our malloc will never fail + // for such a small allocation + NOTIFY_INTERNAL_CRASH(0, "Unable to allocate portproc"); + } + + ns->stat_portproc_alloc++; + + LIST_INIT(&pdata->clients); + pdata->src = src; + pdata->flags = PORT_PROC_FLAGS_NONE; + pdata->pid = proc; + _nc_table_insert_n(&ns->proc_table, &pdata->pid); + LIST_INSERT_HEAD(&pdata->clients, c, client_pid_entry); + + dispatch_set_context(src, pdata); + dispatch_source_set_cancel_handler_f(src, proc_free); + dispatch_activate(src); +} + +static void +port_create(notify_state_t *ns, client_t *c, mach_port_t port) +{ + port_data_t *pdata = calloc(1, sizeof(port_data_t)); + + if (pdata == NULL) { + // failing here is not an option, and our malloc will never fail + // for such a small allocation + NOTIFY_INTERNAL_CRASH(0, "Unable to allocate portproc"); + } + + ns->stat_portproc_alloc++; + + LIST_INIT(&pdata->clients); + pdata->port = port; + _nc_table_insert_n(&ns->port_table, &pdata->port); + LIST_INSERT_HEAD(&pdata->clients, c, client_port_entry); + + mach_port_insert_right(mach_task_self(), port, port, + MACH_MSG_TYPE_COPY_SEND); + /* arming SEND_POSSIBLE must be done before we attempt any send */ + port_arm_mach_notifications(port); +} + +static bool +proc_register(notify_state_t *ns, client_t *c, pid_t pid) +{ + proc_data_t *pdata = _nc_table_find_n(&ns->proc_table, pid); + if (pdata) { + LIST_INSERT_HEAD(&pdata->clients, c, client_pid_entry); + } + return pdata != NULL; +} + +static bool +port_register(notify_state_t *ns, client_t *c, mach_port_t port) +{ + port_data_t *pdata = _nc_table_find_n(&ns->port_table, port); + if (pdata) { + LIST_INSERT_HEAD(&pdata->clients, c, client_port_entry); + } + return pdata != NULL; +} + +static void +proc_resume(void *_pp) +{ + proc_data_t *pdata = _pp; + client_t *c; + + pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED; + + LIST_FOREACH(c, &pdata->clients, client_pid_entry) { + _notify_lib_resume_client(&global.notify_state, c, pdata, NULL); + } +} + +/* + * a.k.a. port_resume + * + * receiving a send-possible on a port + * causes us to resume sending to it. + */ +kern_return_t +do_mach_notify_send_possible(mach_port_t notify, mach_port_name_t port) +{ + port_data_t *pdata; + client_t *c; + + /* the notification was consumed, re-arm it */ + port_arm_mach_notifications(port); + + pdata = _nc_table_find_n(&global.notify_state.port_table, port); + pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED; + + LIST_FOREACH(c, &pdata->clients, client_port_entry) { + _notify_lib_resume_client(&global.notify_state, c, NULL, pdata); + /* + * If a send failed again, there's not point to keep trying, + * just wait for the send-possible to be issued. + */ + if (pdata->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED) { + break; + } + } + + return KERN_SUCCESS; +} + + +static void +port_proc_cancel_client(client_t *c) { name_info_t *n; - int token; - - if (global.notify_state == NULL) return; - if (c == NULL) return; call_statistics.cleanup++; - token = c->client_id; - - if (c->private != NULL) + if (c->service_index != 0) { - service_close(c->private); - c->private = NULL; + service_close(c->service_index); + c->service_index = 0; } n = c->name_info; - if ((n != NULL) && (n->refcount == 1) && (n->private != NULL)) + assert(n != NULL); + if ((n->refcount == 1) && (n->private != NULL)) { service_close(n->private); n->private = NULL; } - _notify_lib_port_proc_release(global.notify_state, MACH_PORT_NULL, c->pid); - - if (c->notify_type == NOTIFY_TYPE_MEMORY) + if (c->state_and_type & NOTIFY_TYPE_MEMORY) { global.shared_memory_refcount[n->slot]--; } - else if (c->notify_type == NOTIFY_TYPE_PORT) + else if (c->state_and_type & NOTIFY_TYPE_PORT) { - _notify_lib_port_proc_release(global.notify_state, c->port, 0); + LIST_REMOVE(c, client_port_entry); + } + LIST_REMOVE(c, client_pid_entry); + + _notify_lib_cancel_client(&global.notify_state, c); +} + +static inline void +proc_cancel(proc_data_t *pdata) +{ + client_t *c, *tmp; + + LIST_FOREACH_SAFE(c, &pdata->clients, client_pid_entry, tmp) { + port_proc_cancel_client(c); } - _notify_lib_cancel(global.notify_state, c->pid, token); + dispatch_source_cancel(pdata->src); +} + +/* + * a.k.a. port_cancel + * + * receiving a dead-name for a port causes us canceling it + */ +kern_return_t +do_mach_notify_dead_name(mach_port_t notify, mach_port_name_t port) +{ + port_data_t *pdata; + client_t *c, *tmp; + + pdata = _nc_table_find_n(&global.notify_state.port_table, port); + LIST_FOREACH_SAFE(c, &pdata->clients, client_port_entry, tmp) { + port_proc_cancel_client(c); + } + port_free(pdata); + + // the act of receiving a dead name notification allocates a dead-name + // right that must be deallocated + mach_port_deallocate(mach_task_self(), port); + return KERN_SUCCESS; +} + + +static void +proc_event(void *_pp) +{ + proc_data_t *pdata = _pp; + + proc_cancel(pdata); } static void -cancel_proc(void *px) +register_proc(client_t *c, pid_t pid) { - void *tt; - long lpid = (long)px; - pid_t pid; - client_t *c; - list_t *l, *x; + dispatch_source_t src; - if (global.notify_state == NULL) return; + if (pid <= 0) return; - pid = lpid; - x = NULL; - - tt = _nc_table_traverse_start(global.notify_state->client_table); - while (tt != NULL) - { - c = _nc_table_traverse(global.notify_state->client_table, tt); - if (c == NULL) break; - - if (c->pid == pid) x = _nc_list_prepend(x, _nc_list_new(c)); - } - _nc_table_traverse_end(global.notify_state->client_table, tt); - - for (l = x; l != NULL; l = _nc_list_next(l)) - { - c = _nc_list_data(l); - cancel_subscription(c); - } - - _nc_list_release_list(x); -} - -static void -cancel_port(mach_port_t port, dispatch_source_t src) -{ - void *tt; - client_t *c; - list_t *l, *x; - - if (global.notify_state == NULL) return; - - x = NULL; - - tt = _nc_table_traverse_start(global.notify_state->client_table); - while (tt != NULL) - { - c = _nc_table_traverse(global.notify_state->client_table, tt); - if (c == NULL) break; - - if (c->port == port) x = _nc_list_prepend(x, _nc_list_new(c)); - } - _nc_table_traverse_end(global.notify_state->client_table, tt); - - for (l = x; l != NULL; l = _nc_list_next(l)) - { - c = _nc_list_data(l); - cancel_subscription(c); - } - - _nc_list_release_list(x); -} - -static void -port_event(void *px) -{ - long lport = (long)px; - mach_port_t port; - unsigned long data; - portproc_data_t *pp; - - if (global.notify_state == NULL) return; - - port = (mach_port_t)lport; - if (port == MACH_PORT_NULL) return; - - pp = _notify_lib_port_proc_find(global.notify_state, port, 0); - if (pp == NULL) - { - log_message(ASL_LEVEL_DEBUG, "can't find port source for %u\n", port); + if (proc_register(&global.notify_state, c, pid)) { return; } - data = dispatch_source_get_data(pp->src); + src = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, + DISPATCH_PROC_EXIT, global.workloop); + dispatch_source_set_event_handler_f(src, proc_event); - if (data & DISPATCH_MACH_SEND_DEAD) - { - cancel_port(port, pp->src); - } - else if (data & DISPATCH_MACH_SEND_POSSIBLE) - { - _notify_lib_resume_port(global.notify_state, port); - } - else - { - log_message(ASL_LEVEL_DEBUG, "unknown data 0x%lx for %u\n", data, port); + proc_create(&global.notify_state, c, pid, src); +} + +static void +register_port(client_t *c, mach_port_t port) +{ + if (port_register(&global.notify_state, c, port)) { + return; } - _notify_lib_port_proc_release(global.notify_state, port, 0); + port_create(&global.notify_state, c, port); } -static void -port_dealloc(void *px) -{ - long lport = (long)px; - mach_port_t port = (mach_port_t)lport; - - if (port == MACH_PORT_NULL) return; - mach_port_deallocate(mach_task_self(), port); -} - -static void -port_registration_complete(void *px) -{ - long lport = (long)px; - mach_port_t port; - - if (global.notify_state == NULL) return; - - port = (mach_port_t)lport; - _notify_lib_resume_port(global.notify_state, port); -} - -static void -register_pid(pid_t pid) -{ - dispatch_source_t src; - long lpid; - portproc_data_t *pp; - - if (global.notify_state == NULL) return; - if (pid <= 0) return; - - /* - * Check if this pid has already registered. - * N.B. This call retains the portproc_data_t. We want that. - */ - pp = _notify_lib_port_proc_find(global.notify_state, MACH_PORT_NULL, pid); - if (pp != NULL) return; - - src = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, global.work_q); - dispatch_source_set_event_handler_f(src, (dispatch_function_t)cancel_proc); - - lpid = pid; - dispatch_set_context(src, (void *)lpid); - - _notify_lib_port_proc_new(global.notify_state, MACH_PORT_NULL, pid, 0, src); - - dispatch_resume(src); -} - -static void -register_port(client_t *c) -{ - dispatch_source_t src; - long lport; - portproc_data_t *pp; - - if (c == NULL) return; - - /* ignore MACH_PORT_DEAD */ - if (c->port == MACH_PORT_DEAD) return; - - /* - * Check if this port has already registered. - * N.B. This call retains the portproc_data_t. We want that. - */ - pp = _notify_lib_port_proc_find(global.notify_state, c->port, 0); - if (pp != NULL) return; - - src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, c->port, DISPATCH_MACH_SEND_DEAD | DISPATCH_MACH_SEND_POSSIBLE, global.work_q); - - dispatch_source_set_event_handler_f(src, (dispatch_function_t)port_event); - - /* retain send right for port - port_dealloc() will release when the source goes away */ - mach_port_mod_refs(mach_task_self(), c->port, MACH_PORT_RIGHT_SEND, +1); - dispatch_source_set_cancel_handler_f(src, (dispatch_function_t)port_dealloc); - - lport = c->port; - dispatch_set_context(src, (void *)lport); - - dispatch_source_set_registration_handler_f(src, (dispatch_function_t)port_registration_complete); - - _notify_lib_port_proc_new(global.notify_state, c->port, 0, NOTIFY_PORT_PROC_STATE_SUSPENDED, src); - - dispatch_resume(src); -} static uint32_t -server_preflight(caddr_t name, mach_msg_type_number_t nameCnt, audit_token_t audit, int token, uid_t *uid, gid_t *gid, pid_t *pid, uint64_t *cid) +string_validate(caddr_t path, mach_msg_type_number_t pathCnt) +{ + if (path == NULL && pathCnt != 0) { + return NOTIFY_STATUS_INVALID_REQUEST; + } + if (path && (pathCnt == 0 || path[pathCnt - 1] != '\0')) { + return NOTIFY_STATUS_INVALID_REQUEST; + } + return NOTIFY_STATUS_OK; +} + +static void +server_preflight(audit_token_t audit, int token, uid_t *uid, gid_t *gid, pid_t *pid, uint64_t *cid) { pid_t xpid; - if ((name == NULL) && (nameCnt != 0)) return NOTIFY_STATUS_INVALID_NAME; - - if (global.notify_state == NULL) + if(uid != NULL) { - if (name != NULL) vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - return NOTIFY_STATUS_FAILED; + *uid = audit_token_to_euid(audit); } - if ((name != NULL) && (name[nameCnt] != '\0')) + if(gid != NULL) { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - return NOTIFY_STATUS_INVALID_NAME; + *gid = audit_token_to_egid(audit); } - audit_token_to_au32(audit, NULL, uid, gid, NULL, NULL, &xpid, NULL, NULL); - if (pid != NULL) *pid = xpid; + xpid = audit_token_to_pid(audit); + if (pid != NULL) + { + *pid = xpid; + } if (token > 0) { @@ -295,38 +357,59 @@ server_preflight(caddr_t name, mach_msg_type_number_t nameCnt, audit_token_t aud uint64_t xcid = make_client_id(xpid, token); if (cid != NULL) *cid = xcid; - c = _nc_table_find_64(global.notify_state->client_table, xcid); + c = _nc_table_find_64(&global.notify_state.client_table, xcid); if (c != NULL) { /* duplicate tokens can occur if a process exec()s */ log_message(ASL_LEVEL_DEBUG, "duplicate token %d sent from PID %d\n", token, xpid); - cancel_subscription(c); + port_proc_cancel_client(c); } } - - return NOTIFY_STATUS_OK; } kern_return_t __notify_server_post_3 ( mach_port_t server, uint64_t name_id, + boolean_t claim_root_access, audit_token_t audit ) { uid_t uid = (uid_t)-1; gid_t gid = (gid_t)-1; + pid_t pid = (pid_t)-1; + int status; + name_info_t *n; - if (global.notify_state == NULL) return KERN_SUCCESS; + n = _nc_table_find_64(&global.notify_state.name_id_table, name_id); + if (n == NULL) + { + return KERN_SUCCESS; // No one is registered for the name + } + + n->postcount++; + + server_preflight(audit, -1, &uid, &gid, &pid, NULL); + + if ((uid != 0) && claim_root_access && has_root_entitlement(audit)) + { + uid = 0; + } + + status = _notify_lib_check_controlled_access(&global.notify_state, n->name, uid, gid, NOTIFY_ACCESS_WRITE); + assert(status != NOTIFY_STATUS_NULL_INPUT); + if (status != NOTIFY_STATUS_OK){ + return KERN_SUCCESS; // The poster does not have permission to post + } call_statistics.post++; call_statistics.post_by_id++; - log_message(ASL_LEVEL_DEBUG, "__notify_server_post name_id %llu\n", name_id); + log_message(ASL_LEVEL_DEBUG, "__notify_server_post %s %d by nameid: %llu \n", n->name, pid, name_id); - audit_token_to_au32(audit, NULL, &uid, &gid, NULL, NULL, NULL, NULL, NULL); + status = daemon_post_nid(name_id, uid, gid); + assert(status == NOTIFY_STATUS_OK); - daemon_post_nid(name_id, uid, gid); return KERN_SUCCESS; } @@ -334,37 +417,40 @@ kern_return_t __notify_server_post_2 ( mach_port_t server, caddr_t name, - mach_msg_type_number_t nameCnt, uint64_t *name_id, int *status, + boolean_t claim_root_access, audit_token_t audit ) { uid_t uid = (uid_t)-1; gid_t gid = (gid_t)-1; + pid_t pid = (pid_t)-1; name_info_t *n; *name_id = 0; - *status = server_preflight(name, nameCnt, audit, -1, &uid, &gid, NULL, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + server_preflight(audit, -1, &uid, &gid, &pid, NULL); - call_statistics.post++; - call_statistics.post_by_name_and_fetch_id++; + if ((uid != 0) && claim_root_access && has_root_entitlement(audit)) + { + uid = 0; + } - *status = _notify_lib_check_controlled_access(global.notify_state, name, uid, gid, NOTIFY_ACCESS_WRITE); + *status = _notify_lib_check_controlled_access(&global.notify_state, name, uid, gid, NOTIFY_ACCESS_WRITE); if (*status != NOTIFY_STATUS_OK) { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); return KERN_SUCCESS; } + call_statistics.post++; + call_statistics.post_by_name_and_fetch_id++; + n = NULL; *status = daemon_post(name, uid, gid); - if (*status == NOTIFY_STATUS_OK) - { - n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name); - } + assert(*status != NOTIFY_STATUS_NULL_INPUT); + + n = _nc_table_find(&global.notify_state.name_table, name); if (n == NULL) { @@ -374,13 +460,12 @@ kern_return_t __notify_server_post_2 } else { + n->postcount++; *name_id = n->name_id; } - if (*name_id == UINT64_MAX) log_message(ASL_LEVEL_DEBUG, "__notify_server_post %s\n", name); - else log_message(ASL_LEVEL_DEBUG, "__notify_server_post %s [%llu]\n", name, *name_id); - - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); + if (*name_id == UINT64_MAX) log_message(ASL_LEVEL_DEBUG, "__notify_server_post %s %d\n", name, pid); + else log_message(ASL_LEVEL_DEBUG, "__notify_server_post %s %d [%llu]\n", name, pid, *name_id); return KERN_SUCCESS; } @@ -389,7 +474,7 @@ kern_return_t __notify_server_post_4 ( mach_port_t server, caddr_t name, - mach_msg_type_number_t nameCnt, + boolean_t claim_root_access, audit_token_t audit ) { @@ -397,7 +482,7 @@ kern_return_t __notify_server_post_4 int ignored_status; kern_return_t kstatus; - kstatus = __notify_server_post_2(server, name, nameCnt, &ignored_name_id, &ignored_status, audit); + kstatus = __notify_server_post_2(server, name, &ignored_name_id, &ignored_status, claim_root_access, audit); call_statistics.post_by_name_and_fetch_id--; call_statistics.post_by_name++; @@ -405,35 +490,10 @@ kern_return_t __notify_server_post_4 return kstatus; } -kern_return_t __notify_server_post -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - int *status, - audit_token_t audit -) -{ - uint64_t ignored_name_id; - kern_return_t kstatus; - - *status = NOTIFY_STATUS_OK; - - kstatus = __notify_server_post_2(server, name, nameCnt, &ignored_name_id, status, audit); - - call_statistics.post_by_name_and_fetch_id--; - call_statistics.post_by_name++; - - if (*status == NOTIFY_STATUS_INVALID_NAME) *status = NOTIFY_STATUS_OK; - - return kstatus; -} - kern_return_t __notify_server_register_plain_2 ( mach_port_t server, caddr_t name, - mach_msg_type_number_t nameCnt, int token, audit_token_t audit ) @@ -448,24 +508,21 @@ kern_return_t __notify_server_register_plain_2 call_statistics.reg++; call_statistics.reg_plain++; - status = server_preflight(name, nameCnt, audit, token, &uid, &gid, &pid, &cid); - if (status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + server_preflight(audit, token, &uid, &gid, &pid, &cid); log_message(ASL_LEVEL_DEBUG, "__notify_server_register_plain %s %d %d\n", name, pid, token); - status = _notify_lib_register_plain(global.notify_state, name, pid, token, -1, uid, gid, &nid); + status = _notify_lib_register_plain(&global.notify_state, name, pid, token, -1, uid, gid, &nid); if (status != NOTIFY_STATUS_OK) { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); return KERN_SUCCESS; } - c = _nc_table_find_64(global.notify_state->client_table, cid); + c = _nc_table_find_64(&global.notify_state.client_table, cid); - if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, uid, gid); - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); + if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, audit); - register_pid(pid); + register_proc(c, pid); return KERN_SUCCESS; } @@ -474,7 +531,6 @@ kern_return_t __notify_server_register_check_2 ( mach_port_t server, caddr_t name, - mach_msg_type_number_t nameCnt, int token, int *size, int *slot, @@ -496,8 +552,7 @@ kern_return_t __notify_server_register_check_2 *name_id = 0; *status = NOTIFY_STATUS_OK; - *status = server_preflight(name, nameCnt, audit, token, &uid, &gid, &pid, &cid); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + server_preflight(audit, token, &uid, &gid, &pid, &cid); call_statistics.reg++; call_statistics.reg_check++; @@ -506,12 +561,12 @@ kern_return_t __notify_server_register_check_2 { *size = -1; *slot = -1; - return __notify_server_register_plain_2(server, name, nameCnt, token, audit); + return __notify_server_register_plain_2(server, name, token, audit); } x = (uint32_t)-1; - n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name); + n = _nc_table_find(&global.notify_state.name_table, name); if (n != NULL) x = n->slot; new_slot = 0; @@ -564,19 +619,17 @@ kern_return_t __notify_server_register_check_2 *size = global.nslots * sizeof(uint32_t); *slot = x; - *status = _notify_lib_register_plain(global.notify_state, name, pid, token, x, uid, gid, name_id); + *status = _notify_lib_register_plain(&global.notify_state, name, pid, token, x, uid, gid, name_id); if (*status != NOTIFY_STATUS_OK) { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); return KERN_SUCCESS; } - c = _nc_table_find_64(global.notify_state->client_table, cid); + c = _nc_table_find_64(&global.notify_state.client_table, cid); - if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, uid, gid); - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); + if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, audit); - register_pid(pid); + register_proc(c, pid); return KERN_SUCCESS; } @@ -585,7 +638,6 @@ kern_return_t __notify_server_register_signal_2 ( mach_port_t server, caddr_t name, - mach_msg_type_number_t nameCnt, int token, int sig, audit_token_t audit @@ -598,27 +650,24 @@ kern_return_t __notify_server_register_signal_2 gid_t gid = (gid_t)-1; pid_t pid = (pid_t)-1; - status = server_preflight(name, nameCnt, audit, token, &uid, &gid, &pid, &cid); - if (status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + server_preflight(audit, token, &uid, &gid, &pid, &cid); call_statistics.reg++; call_statistics.reg_signal++; log_message(ASL_LEVEL_DEBUG, "__notify_server_register_signal %s %d %d %d\n", name, pid, token, sig); - status = _notify_lib_register_signal(global.notify_state, name, pid, token, sig, uid, gid, &name_id); + status = _notify_lib_register_signal(&global.notify_state, name, pid, token, sig, uid, gid, &name_id); if (status != NOTIFY_STATUS_OK) { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); return KERN_SUCCESS; } - c = _nc_table_find_64(global.notify_state->client_table, cid); + c = _nc_table_find_64(&global.notify_state.client_table, cid); - if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, uid, gid); - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); + if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, audit); - register_pid(pid); + register_proc(c, pid); return KERN_SUCCESS; } @@ -627,7 +676,6 @@ kern_return_t __notify_server_register_file_descriptor_2 ( mach_port_t server, caddr_t name, - mach_msg_type_number_t nameCnt, int token, fileport_t fileport, audit_token_t audit @@ -641,8 +689,7 @@ kern_return_t __notify_server_register_file_descriptor_2 gid_t gid = (gid_t)-1; pid_t pid = (pid_t)-1; - status = server_preflight(name, nameCnt, audit, token, &uid, &gid, &pid, &cid); - if (status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + server_preflight(audit, token, &uid, &gid, &pid, &cid); call_statistics.reg++; call_statistics.reg_file++; @@ -653,37 +700,32 @@ kern_return_t __notify_server_register_file_descriptor_2 mach_port_deallocate(mach_task_self(), fileport); if (fd < 0) { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); return KERN_SUCCESS; } flags = fcntl(fd, F_GETFL, 0); if (flags < 0) { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); return KERN_SUCCESS; } flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) < 0) { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); return KERN_SUCCESS; } - status = _notify_lib_register_file_descriptor(global.notify_state, name, pid, token, fd, uid, gid, &name_id); + status = _notify_lib_register_file_descriptor(&global.notify_state, name, pid, token, fd, uid, gid, &name_id); if (status != NOTIFY_STATUS_OK) { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); return KERN_SUCCESS; } - c = _nc_table_find_64(global.notify_state->client_table, cid); + c = _nc_table_find_64(&global.notify_state.client_table, cid); - if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, uid, gid); - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); + if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, audit); - register_pid(pid); + register_proc(c, pid); return KERN_SUCCESS; } @@ -692,7 +734,6 @@ kern_return_t __notify_server_register_mach_port_2 ( mach_port_t server, caddr_t name, - mach_msg_type_number_t nameCnt, int token, mach_port_t port, audit_token_t audit @@ -705,63 +746,31 @@ kern_return_t __notify_server_register_mach_port_2 gid_t gid = (gid_t)-1; pid_t pid = (pid_t)-1; - if (port == MACH_PORT_DEAD) return KERN_SUCCESS; - - status = server_preflight(name, nameCnt, audit, token, &uid, &gid, &pid, &cid); - if (status != NOTIFY_STATUS_OK) + if (port == MACH_PORT_DEAD || port == MACH_PORT_NULL) { - mach_port_deallocate(mach_task_self(), port); return KERN_SUCCESS; } + server_preflight(audit, token, &uid, &gid, &pid, &cid); + call_statistics.reg++; call_statistics.reg_port++; log_message(ASL_LEVEL_DEBUG, "__notify_server_register_mach_port %s %d %d\n", name, pid, token); - status = _notify_lib_register_mach_port(global.notify_state, name, pid, token, port, uid, gid, &name_id); + status = _notify_lib_register_mach_port(&global.notify_state, name, pid, token, port, uid, gid, &name_id); if (status != NOTIFY_STATUS_OK) { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); mach_port_deallocate(mach_task_self(), port); return KERN_SUCCESS; } - c = _nc_table_find_64(global.notify_state->client_table,cid); + c = _nc_table_find_64(&global.notify_state.client_table,cid); - if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, uid, gid); - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); + if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, audit); - register_pid(pid); - register_port(c); - - return KERN_SUCCESS; -} - -kern_return_t __notify_server_cancel -( - mach_port_t server, - int token, - int *status, - audit_token_t audit -) -{ - client_t *c; - uid_t uid = (uid_t)-1; - pid_t pid = (pid_t)-1; - - *status = server_preflight(NULL, 0, audit, -1, &uid, NULL, &pid, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; - - call_statistics.cancel++; - - log_message(ASL_LEVEL_DEBUG, "__notify_server_cancel %d %d\n", pid, token); - - *status = NOTIFY_STATUS_OK; - - c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token)); - if (c == NULL) *status = NOTIFY_STATUS_FAILED; - else cancel_subscription(c); + register_proc(c, pid); + register_port(c, port); return KERN_SUCCESS; } @@ -773,8 +782,22 @@ kern_return_t __notify_server_cancel_2 audit_token_t audit ) { - int ignored; - return __notify_server_cancel(server, token, &ignored, audit); + client_t *c; + uid_t uid = (uid_t)-1; + pid_t pid = (pid_t)-1; + + server_preflight(audit, -1, &uid, NULL, &pid, NULL); + + call_statistics.cancel++; + + log_message(ASL_LEVEL_DEBUG, "__notify_server_cancel %d %d\n", pid, token); + + c = _nc_table_find_64(&global.notify_state.client_table, make_client_id(pid, token)); + if (c != NULL) { + port_proc_cancel_client(c); + } + + return KERN_SUCCESS; } kern_return_t __notify_server_suspend @@ -787,15 +810,14 @@ kern_return_t __notify_server_suspend { pid_t pid = (pid_t)-1; - *status = server_preflight(NULL, 0, audit, -1, NULL, NULL, &pid, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + server_preflight(audit, -1, NULL, NULL, &pid, NULL); call_statistics.suspend++; log_message(ASL_LEVEL_DEBUG, "__notify_server_suspend %d %d\n", pid, token); - _notify_lib_suspend(global.notify_state, pid, token); - *status = NOTIFY_STATUS_OK; + *status = _notify_lib_suspend(&global.notify_state, pid, token); + assert(*status != NOTIFY_STATUS_NULL_INPUT); return KERN_SUCCESS; } @@ -810,67 +832,44 @@ kern_return_t __notify_server_resume { pid_t pid = (pid_t)-1; - *status = server_preflight(NULL, 0, audit, -1, NULL, NULL, &pid, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + server_preflight(audit, -1, NULL, NULL, &pid, NULL); call_statistics.resume++; log_message(ASL_LEVEL_DEBUG, "__notify_server_resume %d %d\n", pid, token); - _notify_lib_resume(global.notify_state, pid, token); - *status = NOTIFY_STATUS_OK; + *status = _notify_lib_resume(&global.notify_state, pid, token); + assert(*status != NOTIFY_STATUS_NULL_INPUT); return KERN_SUCCESS; } -static uid_t -uid_for_pid(pid_t pid) -{ - int mib[4]; - struct kinfo_proc info; - size_t size = sizeof(struct kinfo_proc); - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = pid; - - sysctl(mib, 4, &info, &size, 0, 0); - - return (uid_t)info.kp_eproc.e_ucred.cr_uid; -} - kern_return_t __notify_server_suspend_pid ( mach_port_t server, int pid, - int *status, audit_token_t audit ) { - uid_t uid, target_uid; + uid_t uid; + proc_data_t *pdata; - uid = (uid_t)-1; - - *status = server_preflight(NULL, 0, audit, -1, &uid, NULL, NULL, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + uid = audit_token_to_euid(audit); call_statistics.suspend_pid++; log_message(ASL_LEVEL_DEBUG, "__notify_server_suspend_pid %d\n", pid); - target_uid = uid_for_pid(pid); - - if ((uid != 0) && (target_uid != uid)) + if (uid != 0) { - *status = NOTIFY_STATUS_NOT_AUTHORIZED; + log_message(ASL_LEVEL_WARNING, "__notify_server_suspend_pid %d permission denied for caller %d \n", pid, audit_token_to_pid(audit)); return KERN_SUCCESS; } - *status = NOTIFY_STATUS_OK; - - _notify_lib_suspend_proc(global.notify_state, pid); - + pdata = _nc_table_find_n(&global.notify_state.proc_table, pid); + if (pdata) { + pdata->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED; + } return KERN_SUCCESS; } @@ -878,33 +877,28 @@ kern_return_t __notify_server_resume_pid ( mach_port_t server, int pid, - int *status, audit_token_t audit ) { - uid_t uid, target_uid; + uid_t uid; + proc_data_t *pdata; - uid = (uid_t)-1; - - *status = server_preflight(NULL, 0, audit, -1, &uid, NULL, NULL, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + uid = audit_token_to_euid(audit); call_statistics.resume_pid++; log_message(ASL_LEVEL_DEBUG, "__notify_server_resume_pid %d\n", pid); - target_uid = uid_for_pid(pid); - - if ((uid != 0) && (target_uid != uid)) + if (uid != 0) { - *status = NOTIFY_STATUS_NOT_AUTHORIZED; + log_message(ASL_LEVEL_WARNING, "__notify_server_resume_pid %d permission denied for caller %d \n", pid, audit_token_to_pid(audit)); return KERN_SUCCESS; } - *status = NOTIFY_STATUS_OK; - - _notify_lib_resume_proc(global.notify_state, pid); - + pdata = _nc_table_find_n(&global.notify_state.proc_table, pid); + if (pdata) { + proc_resume(pdata); + } return KERN_SUCCESS; } @@ -927,7 +921,9 @@ kern_return_t __notify_server_check log_message(ASL_LEVEL_DEBUG, "__notify_server_check %d %d\n", pid, token); - *status = _notify_lib_check(global.notify_state, pid, token, check); + *status = _notify_lib_check(&global.notify_state, pid, token, check); + assert(*status != NOTIFY_STATUS_NULL_INPUT); + return KERN_SUCCESS; } @@ -947,22 +943,22 @@ kern_return_t __notify_server_get_state *state = 0; - *status = server_preflight(NULL, 0, audit, -1, &uid, &gid, &pid, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + server_preflight(audit, -1, &uid, &gid, &pid, NULL); call_statistics.get_state++; call_statistics.get_state_by_client++; log_message(ASL_LEVEL_DEBUG, "__notify_server_get_state %d %d\n", pid, token); - c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token)); - if ((c == NULL) || (c->name_info == NULL)) + c = _nc_table_find_64(&global.notify_state.client_table, make_client_id(pid, token)); + if (c == NULL) { - *status = NOTIFY_STATUS_FAILED; + *status = NOTIFY_STATUS_CLIENT_NOT_FOUND; } else { - *status = _notify_lib_get_state(global.notify_state, c->name_info->name_id, state, uid, gid); + *status = _notify_lib_get_state(&global.notify_state, c->name_info->name_id, state, uid, gid); + assert(*status == NOTIFY_STATUS_OK || *status == NOTIFY_STATUS_NOT_AUTHORIZED); } return KERN_SUCCESS; @@ -982,12 +978,12 @@ kern_return_t __notify_server_get_state_2 *state = 0; - *status = server_preflight(NULL, 0, audit, -1, &uid, &gid, NULL, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + server_preflight(audit, -1, &uid, &gid, NULL, NULL); log_message(ASL_LEVEL_DEBUG, "__notify_server_get_state_2 %llu\n", name_id); - *status = _notify_lib_get_state(global.notify_state, name_id, state, uid, gid); + *status = _notify_lib_get_state(&global.notify_state, name_id, state, uid, gid); + return KERN_SUCCESS; } @@ -1009,21 +1005,22 @@ kern_return_t __notify_server_get_state_3 *state = 0; *name_id = 0; - *status = server_preflight(NULL, 0, audit, -1, &uid, &gid, &pid, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + server_preflight(audit, -1, &uid, &gid, &pid, NULL); call_statistics.get_state++; call_statistics.get_state_by_client_and_fetch_id++; - c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token)); - if ((c == NULL) || (c->name_info == NULL)) + c = _nc_table_find_64(&global.notify_state.client_table, make_client_id(pid, token)); + if (c == NULL) { - *status = NOTIFY_STATUS_FAILED; + *status = NOTIFY_STATUS_CLIENT_NOT_FOUND; *name_id = UINT64_MAX; } else { - *status = _notify_lib_get_state(global.notify_state, c->name_info->name_id, state, uid, gid); + *status = _notify_lib_get_state(&global.notify_state, c->name_info->name_id, state, uid, gid); + assert(*status == NOTIFY_STATUS_OK || *status == NOTIFY_STATUS_NOT_AUTHORIZED); + *name_id = c->name_info->name_id; } @@ -1040,6 +1037,7 @@ kern_return_t __notify_server_set_state_3 uint64_t state, uint64_t *name_id, int *status, + boolean_t claim_root_access, audit_token_t audit ) { @@ -1050,254 +1048,65 @@ kern_return_t __notify_server_set_state_3 *name_id = 0; - *status = server_preflight(NULL, 0, audit, -1, &uid, &gid, &pid, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; + server_preflight(audit, -1, &uid, &gid, &pid, NULL); + + bool root_entitlement = uid != 0 && claim_root_access && has_root_entitlement(audit); + if (root_entitlement) uid = 0; call_statistics.set_state++; call_statistics.set_state_by_client_and_fetch_id++; - c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token)); - if ((c == NULL) || (c->name_info == NULL)) + c = _nc_table_find_64(&global.notify_state.client_table, make_client_id(pid, token)); + if (c == NULL) { - *status = NOTIFY_STATUS_FAILED; + *status = NOTIFY_STATUS_CLIENT_NOT_FOUND; *name_id = UINT64_MAX; } else { - *status = _notify_lib_set_state(global.notify_state, c->name_info->name_id, state, uid, gid); + assert(c->name_info != NULL); + *status = _notify_lib_set_state(&global.notify_state, c->name_info->name_id, state, uid, gid); + assert(*status == NOTIFY_STATUS_OK || *status == NOTIFY_STATUS_NOT_AUTHORIZED); + *name_id = c->name_info->name_id; } - if (*name_id == UINT64_MAX) log_message(ASL_LEVEL_DEBUG, "__notify_server_set_state_3 %d %d %llu\n", pid, token, state); - else log_message(ASL_LEVEL_DEBUG, "__notify_server_set_state_3 %d %d %llu [%llu]\n", pid, token, state, *name_id); + if (*name_id == UINT64_MAX) log_message(ASL_LEVEL_DEBUG, "__notify_server_set_state_3 %d %d %llu [uid %d%s gid %d]\n", pid, token, state, uid, root_entitlement ? " (entitlement)" : "", gid); + else log_message(ASL_LEVEL_DEBUG, "__notify_server_set_state_3 %d %d %llu [%llu] [uid %d%s gid %d]\n", pid, token, state, *name_id, uid, root_entitlement ? " (entitlement)" : "", gid); return KERN_SUCCESS; } -kern_return_t __notify_server_set_state -( - mach_port_t server, - int token, - uint64_t state, - int *status, - audit_token_t audit -) -{ - uint64_t ignored; - kern_return_t kstatus; - - *status = NOTIFY_STATUS_OK; - - kstatus = __notify_server_set_state_3(server, token, state, &ignored, status, audit); - - call_statistics.set_state_by_client_and_fetch_id--; - call_statistics.set_state_by_client++; - - return kstatus; -} - kern_return_t __notify_server_set_state_2 ( mach_port_t server, uint64_t name_id, uint64_t state, + boolean_t claim_root_access, audit_token_t audit ) { uint32_t status; uid_t uid = (uid_t)-1; gid_t gid = (gid_t)-1; - - if (global.notify_state == NULL) return KERN_SUCCESS; + pid_t pid = (pid_t)-1; call_statistics.set_state++; call_statistics.set_state_by_id++; - audit_token_to_au32(audit, NULL, &uid, &gid, NULL, NULL, NULL, NULL, NULL); + audit_token_to_au32(audit, NULL, &uid, &gid, NULL, NULL, &pid, NULL, NULL); - log_message(ASL_LEVEL_DEBUG, "__notify_server_set_state_2 %llu %llu\n", name_id, state); + bool root_entitlement = (uid != 0) && claim_root_access && has_root_entitlement(audit); + if (root_entitlement) uid = 0; - status = _notify_lib_set_state(global.notify_state, name_id, state, uid, gid); - return KERN_SUCCESS; -} + status = _notify_lib_set_state(&global.notify_state, name_id, state, uid, gid); -kern_return_t __notify_server_set_owner -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - int uid, - int gid, - int *status, - audit_token_t audit -) -{ - uid_t auid = (uid_t)-1; - - *status = server_preflight(name, nameCnt, audit, -1, &auid, NULL, NULL, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; - - call_statistics.set_owner++; - - log_message(ASL_LEVEL_DEBUG, "__notify_server_set_owner %s %d %d\n", name, uid, gid); - - /* only root may set owner for names */ - if (auid != 0) - { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - *status = NOTIFY_STATUS_NOT_AUTHORIZED; - return KERN_SUCCESS; + if(status == NOTIFY_STATUS_OK){ + log_message(ASL_LEVEL_DEBUG, "__notify_server_set_state_2 %d %llu %llu [uid %d%s gid %d]\n", pid, name_id, state, uid, root_entitlement ? " (entitlement)" : "", gid); } - *status = _notify_lib_set_owner(global.notify_state, name, uid, gid); - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - return KERN_SUCCESS; -} - -kern_return_t __notify_server_get_owner -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - int *uid, - int *gid, - int *status, - audit_token_t audit -) -{ - *uid = 0; - *gid = 0; - - *status = server_preflight(name, nameCnt, audit, -1, NULL, NULL, NULL, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; - - call_statistics.get_owner++; - - log_message(ASL_LEVEL_DEBUG, "__notify_server_get_owner %s\n", name); - - *status = _notify_lib_get_owner(global.notify_state, name, (uint32_t *)uid, (uint32_t *)gid); - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - return KERN_SUCCESS; -} - -kern_return_t __notify_server_set_access -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - int mode, - int *status, - audit_token_t audit -) -{ - uint32_t u, g; - uid_t uid = (uid_t)-1; - gid_t gid = (gid_t)-1; - - *status = server_preflight(name, nameCnt, audit, -1, &uid, &gid, NULL, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; - - call_statistics.set_access++; - - log_message(ASL_LEVEL_DEBUG, "__notify_server_set_access %s 0x%03x\n", name, mode); - - _notify_lib_get_owner(global.notify_state, name, &u, &g); - - /* only root and owner may set access for names */ - if ((uid != 0) && (uid != u)) - { - *status = NOTIFY_STATUS_NOT_AUTHORIZED; - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - return KERN_SUCCESS; - } - - *status = _notify_lib_set_access(global.notify_state, name, mode); - if ((u != 0) || (g != 0)) *status = _notify_lib_set_owner(global.notify_state, name, u, g); - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - return KERN_SUCCESS; -} - -kern_return_t __notify_server_get_access -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - int *mode, - int *status, - audit_token_t audit -) -{ - *mode = 0; - - *status = server_preflight(name, nameCnt, audit, -1, NULL, NULL, NULL, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; - - call_statistics.get_access++; - - log_message(ASL_LEVEL_DEBUG, "__notify_server_get_access %s\n", name); - - *status = _notify_lib_get_access(global.notify_state, name, (uint32_t *)mode); - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - return KERN_SUCCESS; -} - -/* Unsupported because it makes no sense */ -kern_return_t __notify_server_release_name -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - int *status, - audit_token_t audit -) -{ - *status = NOTIFY_STATUS_FAILED; - return KERN_SUCCESS; -} - -kern_return_t __notify_server_monitor_file -( - mach_port_t server, - int token, - caddr_t path, - mach_msg_type_number_t pathCnt, - int flags, - int *status, - audit_token_t audit -) -{ - client_t *c; - name_info_t *n; - uid_t uid = (uid_t)-1; - gid_t gid = (gid_t)-1; - pid_t pid = (pid_t)-1; - uint32_t ubits = (uint32_t)flags; - - *status = server_preflight(path, pathCnt, audit, -1, &uid, &gid, &pid, NULL); - if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; - - call_statistics.monitor_file++; - - log_message(ASL_LEVEL_DEBUG, "__notify_server_monitor_file %d %d %s 0x%08x\n", pid, token, path, ubits); - - c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token)); - if (c == NULL) - { - vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt); - *status = NOTIFY_STATUS_INVALID_REQUEST; - return KERN_SUCCESS; - } - - n = c->name_info; - if (n == NULL) - { - vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt); - *status = NOTIFY_STATUS_INVALID_REQUEST; - return KERN_SUCCESS; - } - - *status = service_open_path_private(n->name, c, path, uid, gid, ubits); - vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt); + assert(status == NOTIFY_STATUS_OK || status == NOTIFY_STATUS_NOT_AUTHORIZED || + status == NOTIFY_STATUS_INVALID_NAME); return KERN_SUCCESS; } @@ -1312,180 +1121,41 @@ kern_return_t __notify_server_monitor_file_2 audit_token_t audit ) { - int ignored; - return __notify_server_monitor_file(server, token, path, pathCnt, flags, &ignored, audit); -} - -/* - * Original routines provide compatibility for legacy clients. - * iOS simulator uses them. - */ - -/* - * Generates a integer "token" for legacy client registrations. - */ -static int -generate_token(audit_token_t audit) -{ - static int legacy_id = 0; - - if (++legacy_id == -1) legacy_id = 1; - return legacy_id; -} - -kern_return_t __notify_server_register_plain -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - int *client_id, - int *status, - audit_token_t audit -) -{ - int token = generate_token(audit); - - *client_id = token; - *status = NOTIFY_STATUS_OK; - - return __notify_server_register_plain_2(server, name, nameCnt, token, audit); -} - -kern_return_t __notify_server_register_check -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - int *size, - int *slot, - int *client_id, - int *status, - audit_token_t audit -) -{ - *size = 0; - *slot = 0; - *status = NOTIFY_STATUS_OK; - - uint64_t nid; - int token = generate_token(audit); - - *client_id = token; - - return __notify_server_register_check_2(server, name, nameCnt, token, size, slot, &nid, status, audit); -} - -kern_return_t __notify_server_register_signal -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - int sig, - int *client_id, - int *status, - audit_token_t audit -) -{ - int token = generate_token(audit); - - *client_id = token; - *status = NOTIFY_STATUS_OK; - - return __notify_server_register_signal_2(server, name, nameCnt, token, sig, audit); -} - -kern_return_t __notify_server_register_file_descriptor -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - fileport_t fileport, - int ntoken, - int *client_id, - int *status, - audit_token_t audit -) -{ - kern_return_t kstatus; client_t *c; + name_info_t *n; + uid_t uid = (uid_t)-1; + gid_t gid = (gid_t)-1; pid_t pid = (pid_t)-1; - int token = generate_token(audit); + uint32_t ubits = (uint32_t)flags; + int status; - *client_id = token; - *status = NOTIFY_STATUS_OK; + status = string_validate(path, pathCnt); + if (status != NOTIFY_STATUS_OK) return KERN_SUCCESS; - kstatus = __notify_server_register_file_descriptor_2(server, name, nameCnt, token, fileport, audit); - if (kstatus == KERN_SUCCESS) + server_preflight(audit, -1, &uid, &gid, &pid, NULL); + + call_statistics.monitor_file++; + + log_message(ASL_LEVEL_DEBUG, "__notify_server_monitor_file %d %d %s 0x%08x\n", pid, token, path, ubits); + + c = _nc_table_find_64(&global.notify_state.client_table, make_client_id(pid, token)); + if (c == NULL) { - audit_token_to_au32(audit, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL); - c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token)); - if (c == NULL) *status = NOTIFY_STATUS_FAILED; - else c->send_val = ntoken; - } - - return kstatus; -} - -kern_return_t __notify_server_register_mach_port -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - mach_port_t port, - int ntoken, - int *client_id, - int *status, - audit_token_t audit -) -{ - kern_return_t kstatus; - client_t *c; - pid_t pid = (pid_t)-1; - int token; - - *client_id = 0; - *status = NOTIFY_STATUS_OK; - - if (port == MACH_PORT_DEAD) - { - if ((name != NULL) && (nameCnt > 0)) vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - *status = NOTIFY_STATUS_INVALID_REQUEST; return KERN_SUCCESS; } - token = generate_token(audit); + n = c->name_info; + assert(n != NULL); - *client_id = token; - *status = NOTIFY_STATUS_OK; + service_open_path_private(n->name, c, path, audit, ubits); - kstatus = __notify_server_register_mach_port_2(server, name, nameCnt, token, port, audit); - if (kstatus == KERN_SUCCESS) - { - audit_token_to_au32(audit, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL); - c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token)); - if (c == NULL) *status = NOTIFY_STATUS_FAILED; - else c->send_val = ntoken; - } - - return kstatus; -} - -kern_return_t __notify_server_simple_post -( - mach_port_t server, - caddr_t name, - mach_msg_type_number_t nameCnt, - audit_token_t audit -) -{ - return __notify_server_post_4(server, name, nameCnt, audit); + return KERN_SUCCESS; } kern_return_t __notify_server_regenerate ( mach_port_t server, caddr_t name, - mach_msg_type_number_t nameCnt, int token, uint32_t reg_type, mach_port_t port, @@ -1513,28 +1183,8 @@ kern_return_t __notify_server_regenerate *new_nid = 0; *status = NOTIFY_STATUS_OK; - if (name == NULL) - { - if (path != NULL) vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt); - *status = NOTIFY_STATUS_INVALID_NAME; - return KERN_SUCCESS; - } - - if (name[nameCnt] != '\0') - { - if (path != NULL) vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt); - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - *status = NOTIFY_STATUS_INVALID_NAME; - return KERN_SUCCESS; - } - - if ((path != NULL) && (path[pathCnt] != '\0')) - { - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt); - *status = NOTIFY_STATUS_INVALID_REQUEST; - return KERN_SUCCESS; - } + *status = string_validate(path, pathCnt); + if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS; call_statistics.regenerate++; @@ -1543,13 +1193,11 @@ kern_return_t __notify_server_regenerate log_message(ASL_LEVEL_DEBUG, "__notify_server_regenerate %s %d %d %d %u %d %d %llu %s %d\n", name, pid, token, reg_type, port, sig, prev_slot, prev_state, path, path_flags); cid = make_client_id(pid, token); - c = (client_t *)_nc_table_find_64(global.notify_state->client_table, cid); + c = _nc_table_find_64(&global.notify_state.client_table, cid); if (c != NULL) { - /* duplicate client - this should never happen */ - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - if ((path != NULL) && (pathCnt > 0)) vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt); - *status = NOTIFY_STATUS_FAILED; + /* duplicate client - this means that a registration interleaved with regeneration; no need to regen */ + *status = NOTIFY_STATUS_DUP_CLIENT; return KERN_SUCCESS; } @@ -1564,7 +1212,7 @@ kern_return_t __notify_server_regenerate return KERN_SUCCESS; } - kstatus = __notify_server_register_check_2(server, name, nameCnt, token, &size, new_slot, new_nid, status, audit); + kstatus = __notify_server_register_check_2(server, name, token, &size, new_slot, new_nid, status, audit); if (*status == NOTIFY_STATUS_OK) { if ((*new_slot != UINT32_MAX) && (global.last_shm_base != NULL)) @@ -1577,26 +1225,24 @@ kern_return_t __notify_server_regenerate } case NOTIFY_TYPE_PLAIN: { - kstatus = __notify_server_register_plain_2(server, name, nameCnt, token, audit); + kstatus = __notify_server_register_plain_2(server, name, token, audit); break; } case NOTIFY_TYPE_PORT: { - kstatus = __notify_server_register_mach_port_2(server, name, nameCnt, token, port, audit); + kstatus = __notify_server_register_mach_port_2(server, name, token, port, audit); break; } case NOTIFY_TYPE_SIGNAL: { - kstatus = __notify_server_register_signal_2(server, name, nameCnt, token, sig, audit); + kstatus = __notify_server_register_signal_2(server, name, token, sig, audit); break; } case NOTIFY_TYPE_FILE: /* fall through */ default: { /* can not regenerate this type */ - vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt); - if ((path != NULL) && (pathCnt > 0)) vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt); - *status = NOTIFY_STATUS_FAILED; + *status = NOTIFY_STATUS_TYPE_ISSUE; return KERN_SUCCESS; } } @@ -1606,10 +1252,10 @@ kern_return_t __notify_server_regenerate __notify_server_monitor_file_2(server, token, path, pathCnt, path_flags, audit); } - c = (client_t *)_nc_table_find_64(global.notify_state->client_table, cid); + c = _nc_table_find_64(&global.notify_state.client_table, cid); if (c == NULL) { - *status = NOTIFY_STATUS_FAILED; + *status = NOTIFY_STATUS_CLIENT_NOT_FOUND; } else { @@ -1621,3 +1267,84 @@ kern_return_t __notify_server_regenerate return KERN_SUCCESS; } + +kern_return_t __notify_server_checkin +( + mach_port_t server, + uint32_t *version, + uint32_t *server_pid, + int *status, + audit_token_t audit +) +{ + pid_t pid = (pid_t)-1; + + call_statistics.checkin++; + + audit_token_to_au32(audit, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL); + + log_message(ASL_LEVEL_DEBUG, "__notify_server_checkin %d\n", pid); + *version = NOTIFY_IPC_VERSION; + *server_pid = getpid(); + *status = NOTIFY_STATUS_OK; + return KERN_SUCCESS; +} + + +kern_return_t __notify_server_dump +( + mach_port_t server, + fileport_t fileport, + audit_token_t audit +) +{ + int fd; + int flags; + + // Only processes with euid 0 can use this spi + if (audit_token_to_euid(audit) != 0) + { + mach_port_deallocate(mach_task_self(), fileport); + return KERN_SUCCESS; + } + + // Things starting with "forbidden-" will be automatically denied for any + // sandboxed process, so if this returns non-zero, then that means the + // process is in a sandbox and thus can't use this api + if (sandbox_check_by_audit_token(audit, "forbidden-remote-device-admin", SANDBOX_FILTER_NONE)) { + mach_port_deallocate(mach_task_self(), fileport); + return KERN_SUCCESS; + } + + if (!has_entitlement(audit, NOTIFY_STATE_ENTITLEMENT)) { + mach_port_deallocate(mach_task_self(), fileport); + return KERN_SUCCESS; + } + + fd = fileport_makefd(fileport); + mach_port_deallocate(mach_task_self(), fileport); + if (fd < 0) + { + return KERN_SUCCESS; + } + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) + { + close(fd); + return KERN_SUCCESS; + } + + flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) < 0) + { + close(fd); + return KERN_SUCCESS; + } + + dump_status(STATUS_REQUEST_SHORT, fd); + + close(fd); + + return KERN_SUCCESS; +} diff --git a/src/libnotify/notifyd/notifyd.c b/src/libnotify/notifyd/notifyd.c index 03ba1a5b5..a51099fe0 100644 --- a/src/libnotify/notifyd/notifyd.c +++ b/src/libnotify/notifyd/notifyd.c @@ -9,7 +9,7 @@ * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,10 +17,11 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ +#include "notifyd.h" #include #include #include @@ -34,19 +35,28 @@ #include #include #include +#include +#include #include #include #include -#include #include +#include +#include + #include "pathwatch.h" -#include "notifyd.h" #include "service.h" #include "pathwatch.h" #include "timer.h" +#include "notify_internal.h" +#include "notify.h" #include "notify_ipc.h" +#include "notify_ipcServer.h" #include "notify_private.h" +#include "notifyServer.h" + +#define CRSetCrashLogMessage(msg) /**/ #define forever for(;;) #define IndexNull -1 @@ -54,19 +64,18 @@ /* Compile flags */ #define RUN_TIME_CHECKS -#if TARGET_IPHONE_SIMULATOR -static const char *_config_file_path; +#if TARGET_OS_SIMULATOR +static char *_config_file_path; #define CONFIG_FILE_PATH _config_file_path -static const char *_debug_log_path; +static char *_debug_log_path; #define DEBUG_LOG_PATH _debug_log_path #else #define CONFIG_FILE_PATH "/etc/notify.conf" #define DEBUG_LOG_PATH "/var/log/notifyd.log" #endif -#define STATUS_REQUEST_SHORT 0 -#define STATUS_REQUEST_LONG 1 + #define N_NOTIFY_TYPES 6 @@ -74,19 +83,28 @@ static int notifyd_token; static char *status_file = NULL; -typedef union -{ - mach_msg_header_t head; - union __RequestUnion__notify_ipc_subsystem request; -} notify_request_msg; +struct global_s global; +struct call_statistics_s call_statistics; -typedef union +static void +notify_reset_stats(void) { - mach_msg_header_t head; - union __ReplyUnion__notify_ipc_subsystem reply; -} notify_reply_msg; + _nc_table_foreach(&global.notify_state.name_table, ^bool (void *_n){ + name_info_t *n = _n; -extern boolean_t notify_ipc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); + n->last_hour_postcount = n->postcount; + n->postcount = 0; + return true; + }); + + global.last_reset_time = time(NULL); +} + +dispatch_queue_t +get_notifyd_workloop(void) +{ + return global.workloop; +} static const char * notify_type_name(uint32_t t) @@ -102,29 +120,24 @@ notify_type_name(uint32_t t) default: return "unknown"; } - return "unknown"; } static void fprint_client(FILE *f, client_t *c) { - int token; - - if (c == NULL) + if (c == NULL) { fprintf(f, "NULL client\n"); return; } - token = c->client_id; - - fprintf(f, "client_id: %llu\n", c->client_id); - fprintf(f, "pid: %d\n", c->pid); - fprintf(f, "token: %d\n", token); + fprintf(f, "client_id: %llu\n", c->cid.hash_key); + fprintf(f, "pid: %d\n", c->cid.pid); + fprintf(f, "token: %d\n", c->cid.token); fprintf(f, "lastval: %u\n", c->lastval); fprintf(f, "suspend_count: %u\n", c->suspend_count); - fprintf(f, "type: %s\n", notify_type_name(c->notify_type)); - switch(c->notify_type) + fprintf(f, "type: %s\n", notify_type_name(c->state_and_type & NOTIFY_TYPE_MASK)); + switch(c->state_and_type & NOTIFY_TYPE_MASK) { case NOTIFY_TYPE_NONE: break; @@ -136,15 +149,15 @@ fprint_client(FILE *f, client_t *c) break; case NOTIFY_TYPE_PORT: - fprintf(f, "mach port: 0x%08x\n", c->port); + fprintf(f, "mach port: 0x%08x\n", c->deliver.port); break; case NOTIFY_TYPE_FILE: - fprintf(f, "fd: %d\n", c->fd); + fprintf(f, "fd: %d\n", c->deliver.fd); break; case NOTIFY_TYPE_SIGNAL: - fprintf(f, "signal: %d\n", c->sig); + fprintf(f, "signal: %d\n", c->deliver.sig); break; default: break; @@ -152,55 +165,81 @@ fprint_client(FILE *f, client_t *c) } static void -fprint_quick_client(FILE *f, client_t *c, int pname) +fprint_quick_client(FILE *f, client_t *c) { - int token; if (c == NULL) return; - token = c->client_id; + //client_id,pid,token,lastval,suspend_count,0,type,type-info + fprintf(f, "%llu,%d,%d,%u,%u,%u,", c->cid.hash_key, c->cid.pid, c->cid.token, + c->lastval, c->suspend_count, 0); - if (pname == 1) fprintf(f, " [%s]", c->name_info->name); - fprintf(f, " %u %u", c->pid, token); - if (c->suspend_count > 0) fprintf(f, " suspend %d", c->suspend_count); - if (c->state & NOTIFY_CLIENT_STATE_PENDING) fprintf(f, " pending"); - if (c->state & NOTIFY_CLIENT_STATE_TIMEOUT) fprintf(f, " timeout"); - fprintf(f, " %s", notify_type_name(c->notify_type)); - if (c->notify_type == NOTIFY_TYPE_SIGNAL) fprintf(f, " %d", c->sig); - fprintf(f, "\n"); + switch(c->state_and_type & NOTIFY_TYPE_MASK) + { + case NOTIFY_TYPE_PORT: + fprintf(f, "port,0x%08x\n", c->deliver.port); + break; + + case NOTIFY_TYPE_FILE: + fprintf(f, "fd,%d\n", c->deliver.fd); + break; + + case NOTIFY_TYPE_SIGNAL: + fprintf(f, "signal,%d\n", c->deliver.sig); + break; + + case NOTIFY_TYPE_MEMORY: + fprintf(f, "check,0\n"); + break; + + case NOTIFY_TYPE_NONE: + case NOTIFY_TYPE_PLAIN: + default: + fprintf(f, "other,0\n"); + break; + } } static void fprint_quick_name_info(FILE *f, name_info_t *n) { - list_t *sl; client_t *c; if (n == NULL) return; - fprintf(f, "\"%s\" uid=%u gid=%u %03x", n->name, n->uid, n->gid, n->access); - if (n->slot != -1) + // name:name\n + // info:id,uid,gid,access,refcount,postcount,lh_postcount,slot,val,state\n + // clients:\n + // + // + // ... + fprintf(f, "name:%s\n", n->name); + fprintf(f, "info:%llu,%u,%u,%03x,%u,%u,%u,", n->name_id, n->uid, n->gid, n->access, n->refcount, n->postcount, + n->last_hour_postcount); + if (n->slot == -1) { - fprintf(f, " slot %u", n->slot); - if (global.shared_memory_refcount[n->slot] != -1) fprintf(f, " = %u", global.shared_memory_base[n->slot]); + fprintf(f, "-1,"); + } + else + { + fprintf(f, "%u,", n->slot); + } + + fprintf(f, "%u,%llu\n", n->val, n->state); + + fprintf(f, "clients:\n"); + + LIST_FOREACH(c, &n->subscriptions, client_subscription_entry) + { + fprint_quick_client(f, c); } fprintf(f, "\n"); - for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl)) - { - c = _nc_list_data(sl); - if (c == NULL) break; - - fprint_quick_client(f, c, 0); - } - - fprintf(f, "\n"); } static void -fprint_name_info(FILE *f, const char *name, name_info_t *n, table_t *pid_table, pid_t *max_pid) +fprint_name_info(FILE *f, const char *name, name_info_t *n, pid_t *max_pid) { - list_t *sl; client_t *c; uint32_t i, reg[N_NOTIFY_TYPES]; @@ -216,6 +255,8 @@ fprint_name_info(FILE *f, const char *name, name_info_t *n, table_t *pid_table, fprintf(f, "gid: %u\n", n->gid); fprintf(f, "access: %03x\n", n->access); fprintf(f, "refcount: %u\n", n->refcount); + fprintf(f, "postcount: %u\n", n->postcount); + fprintf(f, "last hour postcount: %u\n", n->last_hour_postcount); if (n->slot == -1) fprintf(f, "slot: -unassigned-"); else { @@ -229,26 +270,11 @@ fprint_name_info(FILE *f, const char *name, name_info_t *n, table_t *pid_table, for (i = 0; i < N_NOTIFY_TYPES; i++) reg[i] = 0; - for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl)) + LIST_FOREACH(c, &n->subscriptions, client_subscription_entry) { - list_t *l; + if ((c->cid.pid != (pid_t)-1) && (c->cid.pid > *max_pid)) *max_pid = c->cid.pid; - c = _nc_list_data(sl); - if (c == NULL) break; - - if ((c->pid != (pid_t)-1) && (c->pid > *max_pid)) *max_pid = c->pid; - - l = _nc_table_find_n(pid_table, c->pid); - if (l == NULL) - { - _nc_table_insert_n(pid_table, (uint32_t)c->pid, _nc_list_new(c)); - } - else - { - _nc_list_concat(l, _nc_list_new(c)); - } - - switch (c->notify_type) + switch (c->state_and_type & NOTIFY_TYPE_MASK) { case NOTIFY_TYPE_MEMORY: reg[1]++; break; case NOTIFY_TYPE_PLAIN: reg[2]++; break; @@ -261,11 +287,8 @@ fprint_name_info(FILE *f, const char *name, name_info_t *n, table_t *pid_table, fprintf(f, "types: none %u memory %u plain %u port %u file %u signal %u\n", reg[0], reg[1], reg[2], reg[3], reg[4], reg[5]); - for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl)) + LIST_FOREACH(c, &n->subscriptions, client_subscription_entry) { - c = _nc_list_data(sl); - if (c == NULL) break; - fprintf(f, "\n"); fprint_client(f, c); } @@ -274,39 +297,7 @@ fprint_name_info(FILE *f, const char *name, name_info_t *n, table_t *pid_table, static void fprint_quick_status(FILE *f) { - void *tt; - name_info_t *n; - - tt = _nc_table_traverse_start(global.notify_state->name_table); - - while (tt != NULL) - { - n = _nc_table_traverse(global.notify_state->name_table, tt); - if (n == NULL) break; - fprint_quick_name_info(f, n); - } - - _nc_table_traverse_end(global.notify_state->name_table, tt); - fprintf(f, "\n"); -} - -static void -fprint_status(FILE *f) -{ - void *tt; - name_info_t *n; int32_t i; - client_t *c; - svc_info_t *info; - path_node_t *node; - timer_t *timer; - table_t *pid_table; - pid_t pid, max_pid; - uint32_t count; - portproc_data_t *pdata; - - pid_table = _nc_table_new(0); - max_pid = 0; fprintf(f, "--- GLOBALS ---\n"); fprintf(f, "%u slots (current id %u)\n", global.nslots, global.slot_id); @@ -331,6 +322,7 @@ fprint_status(FILE *f) fprintf(f, "cancel %llu\n", call_statistics.cancel); fprintf(f, "cleanup %llu\n", call_statistics.cleanup); fprintf(f, "regenerate %llu\n", call_statistics.regenerate); + fprintf(f, "checkin %llu\n", call_statistics.checkin); fprintf(f, "\n"); fprintf(f, "suspend %llu\n", call_statistics.suspend); fprintf(f, "resume %llu\n", call_statistics.resume); @@ -347,96 +339,61 @@ fprint_status(FILE *f) fprintf(f, " client %llu\n", call_statistics.set_state_by_client); fprintf(f, " fetch %llu\n", call_statistics.set_state_by_client_and_fetch_id); fprintf(f, "\n"); - fprintf(f, "get_owner %llu\n", call_statistics.get_owner); fprintf(f, "set_owner %llu\n", call_statistics.set_owner); fprintf(f, "\n"); - fprintf(f, "get_access %llu\n", call_statistics.get_access); fprintf(f, "set_access %llu\n", call_statistics.set_access); fprintf(f, "\n"); fprintf(f, "monitor %llu\n", call_statistics.monitor_file); fprintf(f, "svc_path %llu\n", call_statistics.service_path); fprintf(f, "svc_timer %llu\n", call_statistics.service_timer); + { + char buf[128]; + strftime(buf, 128, "%a, %d %b %Y %T %z", localtime(&global.last_reset_time)); + fprintf(f, "last reset time was %s\n", buf); + } + fprintf(f, "\n"); - fprintf(f, "name alloc %9u free %9u extant %9u\n", global.notify_state->stat_name_alloc , global.notify_state->stat_name_free, global.notify_state->stat_name_alloc - global.notify_state->stat_name_free); - fprintf(f, "subscription alloc %9u free %9u extant %9u\n", global.notify_state->stat_client_alloc , global.notify_state->stat_client_free, global.notify_state->stat_client_alloc - global.notify_state->stat_client_free); - fprintf(f, "portproc alloc %9u free %9u extant %9u\n", global.notify_state->stat_portproc_alloc , global.notify_state->stat_portproc_free, global.notify_state->stat_portproc_alloc - global.notify_state->stat_portproc_free); + fprintf(f, "name alloc %9u free %9u extant %9u\n", global.notify_state.stat_name_alloc , global.notify_state.stat_name_free, global.notify_state.stat_name_alloc - global.notify_state.stat_name_free); + fprintf(f, "subscription alloc %9u free %9u extant %9u\n", global.notify_state.stat_client_alloc , global.notify_state.stat_client_free, global.notify_state.stat_client_alloc - global.notify_state.stat_client_free); + fprintf(f, "portproc alloc %9u free %9u extant %9u\n", global.notify_state.stat_portproc_alloc , global.notify_state.stat_portproc_free, global.notify_state.stat_portproc_alloc - global.notify_state.stat_portproc_free); fprintf(f, "\n"); - count = 0; - tt = _nc_table_traverse_start(global.notify_state->port_table); - while (tt != NULL) - { - pdata = _nc_table_traverse(global.notify_state->port_table, tt); - if (pdata == NULL) break; - count++; - } - _nc_table_traverse_end(global.notify_state->port_table, tt); - fprintf(f, "port count %u\n", count); - - count = 0; - tt = _nc_table_traverse_start(global.notify_state->proc_table); - while (tt != NULL) - { - pdata = _nc_table_traverse(global.notify_state->proc_table, tt); - if (pdata == NULL) break; - count++; - } - _nc_table_traverse_end(global.notify_state->proc_table, tt); - fprintf(f, "proc count %u\n", count); + fprintf(f, "port count %u\n", global.notify_state.port_table.count); + fprintf(f, "proc count %u\n", global.notify_state.proc_table.count); fprintf(f, "\n"); fprintf(f, "--- NAME TABLE ---\n"); - count = 0; - tt = _nc_table_traverse_start(global.notify_state->name_table); + fprintf(f, "Name Info: id, uid, gid, access, refcount, postcount, last hour postcount, slot, val, state\n"); + fprintf(f, "Client Info: client_id, pid,token, lastval, suspend_count, 0, 0, type, type-info\n\n\n"); - while (tt != NULL) - { - n = _nc_table_traverse(global.notify_state->name_table, tt); - if (n == NULL) break; - fprint_name_info(f, n->name, n, pid_table, &max_pid); + _nc_table_foreach(&global.notify_state.name_table, ^bool(void *n) { + fprint_quick_name_info(f, n); fprintf(f, "\n"); - count++; - } + return true; + }); - fprintf(f, "--- NAME COUNT %u ---\n", count); - _nc_table_traverse_end(global.notify_state->name_table, tt); - fprintf(f, "\n"); - - fprintf(f, "--- SUBSCRIPTION TABLE ---\n"); - count = 0; - tt = _nc_table_traverse_start(global.notify_state->client_table); - - while (tt != NULL) - { - c = _nc_table_traverse(global.notify_state->client_table, tt); - if (c == NULL) break; - fprint_quick_client(f, c, 1); - count++; - } - - fprintf(f, "--- SUBSCRIPTION COUNT %u ---\n", count); - _nc_table_traverse_end(global.notify_state->client_table, tt); + fprintf(f, "--- NAME COUNT %u ---\n", global.notify_state.name_table.count); fprintf(f, "\n"); fprintf(f, "--- CONTROLLED NAME ---\n"); - for (i = 0; i < global.notify_state->controlled_name_count; i++) + for (i = 0; i < global.notify_state.controlled_name_count; i++) { - fprintf(f, "%s %u %u %03x\n", global.notify_state->controlled_name[i]->name, global.notify_state->controlled_name[i]->uid, global.notify_state->controlled_name[i]->gid, global.notify_state->controlled_name[i]->access); + fprintf(f, "%s %u %u %03x\n", global.notify_state.controlled_name[i]->name, global.notify_state.controlled_name[i]->uid, global.notify_state.controlled_name[i]->gid, global.notify_state.controlled_name[i]->access); } - fprintf(f, "--- CONTROLLED NAME COUNT %u ---\n", global.notify_state->controlled_name_count); + fprintf(f, "--- CONTROLLED NAME COUNT %u ---\n", global.notify_state.controlled_name_count); fprintf(f, "\n"); fprintf(f, "--- PUBLIC SERVICE ---\n"); - count = 0; - tt = _nc_table_traverse_start(global.notify_state->name_table); - while (tt != NULL) - { - n = _nc_table_traverse(global.notify_state->name_table, tt); - if (n == NULL) break; - if (n->private == NULL) continue; - count++; + _nc_table_foreach(&global.notify_state.name_table, ^bool(void *_n) { + name_info_t *n = _n; + svc_info_t *info; + path_node_t *node; + timer_t *timer; + + if (n->private == NULL) return true; + info = (svc_info_t *)n->private; if (info->type == 0) @@ -474,33 +431,33 @@ fprint_status(FILE *f) { fprintf(f, "Unknown service: %s (%u)\n", n->name, info->type); } - } + return true; + }); - fprintf(f, "--- PUBLIC SERVICE COUNT %u ---\n", count); - _nc_table_traverse_end(global.notify_state->name_table, tt); + fprintf(f, "--- PUBLIC SERVICE COUNT %u ---\n", global.notify_state.name_table.count); fprintf(f, "\n"); fprintf(f, "--- PRIVATE SERVICE ---\n"); - count = 0; - tt = _nc_table_traverse_start(global.notify_state->client_table); - while (tt != NULL) - { - c = _nc_table_traverse(global.notify_state->client_table, tt); - if (c == NULL) break; - if (c->private == NULL) continue; + _nc_table_foreach_64(&global.notify_state.client_table, ^bool(void *_c) { + client_t *c = _c; + svc_info_t *info; + name_info_t *n; + path_node_t *node; + timer_t *timer; - count++; - info = (svc_info_t *)c->private; + if (c->service_index == 0) return true; + + info = (svc_info_t *)service_info_get(c->service_index); n = c->name_info; if (info->type == 0) { - fprintf(f, "PID %u Null service: %s\n", c->pid, n->name); + fprintf(f, "PID %u Null service: %s\n", c->cid.pid, n->name); } if (info->type == SERVICE_TYPE_PATH_PRIVATE) { node = (path_node_t *)info->private; - fprintf(f, "PID %u Path Service: %s <- %s (UID %d GID %d)\n", c->pid, n->name, node->path, node->uid, node->gid); + fprintf(f, "PID %u Path Service: %s <- %s\n", c->cid.pid, n->name, node->path); } else if (info->type == SERVICE_TYPE_TIMER_PRIVATE) { @@ -509,133 +466,317 @@ fprint_status(FILE *f) { case TIME_EVENT_ONESHOT: { - fprintf(f, "PID %u Time Service: %s <- Oneshot %"PRId64"\n", c->pid, n->name, timer->start); + fprintf(f, "PID %u Time Service: %s <- Oneshot %"PRId64"\n", c->cid.pid, n->name, timer->start); break; } case TIME_EVENT_CLOCK: { - fprintf(f, "PID %u Time Service: %s <- Clock start %"PRId64" freq %"PRIu32" end %"PRId64"\n", c->pid, n->name, timer->start, timer->freq, timer->end); + fprintf(f, "PID %u Time Service: %s <- Clock start %"PRId64" freq %"PRIu32" end %"PRId64"\n", c->cid.pid, n->name, timer->start, timer->freq, timer->end); break; } case TIME_EVENT_CAL: { - fprintf(f, "PID %u Time Service: %s <- Calendar start %"PRId64" freq %"PRIu32" end %"PRId64" day %"PRId32"\n", c->pid, n->name, timer->start, timer->freq, timer->end, timer->day); + fprintf(f, "PID %u Time Service: %s <- Calendar start %"PRId64" freq %"PRIu32" end %"PRId64" day %"PRId32"\n", c->cid.pid, n->name, timer->start, timer->freq, timer->end, timer->day); break; } } } + return true; + }); + + fprintf(f, "--- PRIVATE SERVICE COUNT %u ---\n", global.notify_state.client_table.count); + fprintf(f, "\n"); + +} + +static void +fprint_status(FILE *f) +{ + __block pid_t pid, max_pid; + int32_t i; + + max_pid = 0; + + fprintf(f, "--- GLOBALS ---\n"); + fprintf(f, "%u slots (current id %u)\n", global.nslots, global.slot_id); + fprintf(f, "%u log_cutoff (default %u)\n", global.log_cutoff, global.log_default); + fprintf(f, "\n"); + + fprintf(f, "--- STATISTICS ---\n"); + fprintf(f, "post %llu\n", call_statistics.post); + fprintf(f, " id %llu\n", call_statistics.post_by_id); + fprintf(f, " name %llu\n", call_statistics.post_by_name); + fprintf(f, " fetch %llu\n", call_statistics.post_by_name_and_fetch_id); + fprintf(f, " no_op %llu\n", call_statistics.post_no_op); + fprintf(f, "\n"); + fprintf(f, "register %llu\n", call_statistics.reg); + fprintf(f, " plain %llu\n", call_statistics.reg_plain); + fprintf(f, " check %llu\n", call_statistics.reg_check); + fprintf(f, " signal %llu\n", call_statistics.reg_signal); + fprintf(f, " file %llu\n", call_statistics.reg_file); + fprintf(f, " port %llu\n", call_statistics.reg_port); + fprintf(f, "\n"); + fprintf(f, "check %llu\n", call_statistics.check); + fprintf(f, "cancel %llu\n", call_statistics.cancel); + fprintf(f, "cleanup %llu\n", call_statistics.cleanup); + fprintf(f, "regenerate %llu\n", call_statistics.regenerate); + fprintf(f, "checkin %llu\n", call_statistics.checkin); + fprintf(f, "\n"); + fprintf(f, "suspend %llu\n", call_statistics.suspend); + fprintf(f, "resume %llu\n", call_statistics.resume); + fprintf(f, "suspend_pid %llu\n", call_statistics.suspend_pid); + fprintf(f, "resume_pid %llu\n", call_statistics.resume_pid); + fprintf(f, "\n"); + fprintf(f, "get_state %llu\n", call_statistics.get_state); + fprintf(f, " id %llu\n", call_statistics.get_state_by_id); + fprintf(f, " client %llu\n", call_statistics.get_state_by_client); + fprintf(f, " fetch %llu\n", call_statistics.get_state_by_client_and_fetch_id); + fprintf(f, "\n"); + fprintf(f, "set_state %llu\n", call_statistics.set_state); + fprintf(f, " id %llu\n", call_statistics.set_state_by_id); + fprintf(f, " client %llu\n", call_statistics.set_state_by_client); + fprintf(f, " fetch %llu\n", call_statistics.set_state_by_client_and_fetch_id); + fprintf(f, "\n"); + fprintf(f, "set_owner %llu\n", call_statistics.set_owner); + fprintf(f, "\n"); + fprintf(f, "set_access %llu\n", call_statistics.set_access); + fprintf(f, "\n"); + fprintf(f, "monitor %llu\n", call_statistics.monitor_file); + fprintf(f, "svc_path %llu\n", call_statistics.service_path); + fprintf(f, "svc_timer %llu\n", call_statistics.service_timer); + + + { + char buf[128]; + strftime(buf, 128, "%a, %d %b %Y %T %z", localtime(&global.last_reset_time)); + fprintf(f, "last reset time was %s\n", buf); } - fprintf(f, "--- PRIVATE SERVICE COUNT %u ---\n", count); - _nc_table_traverse_end(global.notify_state->client_table, tt); + fprintf(f, "\n"); + fprintf(f, "name alloc %9u free %9u extant %9u\n", global.notify_state.stat_name_alloc , global.notify_state.stat_name_free, global.notify_state.stat_name_alloc - global.notify_state.stat_name_free); + fprintf(f, "subscription alloc %9u free %9u extant %9u\n", global.notify_state.stat_client_alloc , global.notify_state.stat_client_free, global.notify_state.stat_client_alloc - global.notify_state.stat_client_free); + fprintf(f, "portproc alloc %9u free %9u extant %9u\n", global.notify_state.stat_portproc_alloc , global.notify_state.stat_portproc_free, global.notify_state.stat_portproc_alloc - global.notify_state.stat_portproc_free); + fprintf(f, "\n"); + + fprintf(f, "port count %u\n", global.notify_state.port_table.count); + fprintf(f, "proc count %u\n", global.notify_state.proc_table.count); + fprintf(f, "\n"); + + fprintf(f, "--- NAME TABLE ---\n"); + + _nc_table_foreach(&global.notify_state.name_table, ^bool(void *_n) { + name_info_t *n = _n; + fprint_name_info(f, n->name, n, &max_pid); + fprintf(f, "\n"); + return true; + }); + + fprintf(f, "--- NAME COUNT %u ---\n", global.notify_state.name_table.count); + fprintf(f, "\n"); + + fprintf(f, "--- SUBSCRIPTION TABLE ---\n"); + + _nc_table_foreach_64(&global.notify_state.client_table, ^bool (void *_c) { + client_t *c = _c; + fprintf(f, "%d\n", c->cid.pid); + return true; + }); + + fprintf(f, "--- SUBSCRIPTION COUNT %u ---\n", global.notify_state.client_table.count); + fprintf(f, "\n"); + + fprintf(f, "--- CONTROLLED NAME ---\n"); + for (i = 0; i < global.notify_state.controlled_name_count; i++) + { + fprintf(f, "%s %u %u %03x\n", global.notify_state.controlled_name[i]->name, global.notify_state.controlled_name[i]->uid, global.notify_state.controlled_name[i]->gid, global.notify_state.controlled_name[i]->access); + } + fprintf(f, "--- CONTROLLED NAME COUNT %u ---\n", global.notify_state.controlled_name_count); + fprintf(f, "\n"); + + fprintf(f, "--- PUBLIC SERVICE ---\n"); + + _nc_table_foreach(&global.notify_state.name_table, ^bool(void *_n) { + name_info_t *n = _n; + svc_info_t *info; + path_node_t *node; + timer_t *timer; + + if (n->private == NULL) return true; + + info = (svc_info_t *)n->private; + + if (info->type == 0) + { + fprintf(f, "Null service: %s\n", n->name); + } + if (info->type == SERVICE_TYPE_PATH_PUBLIC) + { + node = (path_node_t *)info->private; + fprintf(f, "Path Service: %s <- %s\n", n->name, node->path); + } + else if (info->type == SERVICE_TYPE_TIMER_PUBLIC) + { + timer = (timer_t *)info->private; + switch (timer->type) + { + case TIME_EVENT_ONESHOT: + { + fprintf(f, "Time Service: %s <- Oneshot %llu\n", n->name, timer->start); + break; + } + case TIME_EVENT_CLOCK: + { + fprintf(f, "Time Service: %s <- Clock start %lld freq %u end %lld\n", n->name, timer->start, timer->freq, timer->end); + break; + } + case TIME_EVENT_CAL: + { + fprintf(f, "Time Service: %s <- Calendar start %lld freq %u end %lld day %d\n", n->name, timer->start, timer->freq, timer->end, timer->day); + break; + } + } + } + else + { + fprintf(f, "Unknown service: %s (%u)\n", n->name, info->type); + } + return true; + }); + + fprintf(f, "--- PUBLIC SERVICE COUNT %u ---\n", global.notify_state.name_table.count); + fprintf(f, "\n"); + + fprintf(f, "--- PRIVATE SERVICE ---\n"); + + _nc_table_foreach_64(&global.notify_state.client_table, ^bool (void *_c) { + client_t *c = _c; + name_info_t *n; + svc_info_t *info; + path_node_t *node; + timer_t *timer; + + if (c->service_index == 0) return true; + + info = (svc_info_t *)service_info_get(c->service_index); + n = c->name_info; + + if (info->type == 0) + { + fprintf(f, "PID %u Null service: %s\n", c->cid.pid, n->name); + } + if (info->type == SERVICE_TYPE_PATH_PRIVATE) + { + node = (path_node_t *)info->private; + fprintf(f, "PID %u Path Service: %s <- %s\n", c->cid.pid, n->name, node->path); + } + else if (info->type == SERVICE_TYPE_TIMER_PRIVATE) + { + timer = (timer_t *)info->private; + switch (timer->type) + { + case TIME_EVENT_ONESHOT: + { + fprintf(f, "PID %u Time Service: %s <- Oneshot %"PRId64"\n", c->cid.pid, n->name, timer->start); + break; + } + case TIME_EVENT_CLOCK: + { + fprintf(f, "PID %u Time Service: %s <- Clock start %"PRId64" freq %"PRIu32" end %"PRId64"\n", c->cid.pid, n->name, timer->start, timer->freq, timer->end); + break; + } + case TIME_EVENT_CAL: + { + fprintf(f, "PID %u Time Service: %s <- Calendar start %"PRId64" freq %"PRIu32" end %"PRId64" day %"PRId32"\n", c->cid.pid, n->name, timer->start, timer->freq, timer->end, timer->day); + break; + } + } + } + return true; + }); + + fprintf(f, "--- PRIVATE SERVICE COUNT %u ---\n", global.notify_state.client_table.count); fprintf(f, "\n"); fprintf(f, "--- PROCESSES ---\n"); for (pid = 0; pid <= max_pid; pid++) { - int mem_count, plain_count, file_count, port_count, sig_count, com_port_count; - mach_port_t common_port = MACH_PORT_NULL; + int mem_count, plain_count, file_count, port_count, sig_count; + proc_data_t *pdata; + client_t *c; - list_t *x; - list_t *l = _nc_table_find_n(pid_table, pid); - if (l == NULL) continue; + pdata = _nc_table_find_n(&global.notify_state.proc_table, pid); + if (pdata == NULL) continue; mem_count = 0; plain_count = 0; file_count = 0; port_count = 0; sig_count = 0; - com_port_count = 0; - for (x = l; x != NULL; x = _nc_list_next(x)) - { - c = _nc_list_data(x); - if (c != NULL) - { - if ((c->notify_type == NOTIFY_TYPE_PORT) && (!strcmp(c->name_info->name, COMMON_PORT_KEY))) common_port = c->port; - } - } + LIST_FOREACH(c, &pdata->clients, client_pid_entry) { + switch(c->state_and_type & NOTIFY_TYPE_MASK) { + case NOTIFY_TYPE_NONE: + break; - for (x = l; x != NULL; x = _nc_list_next(x)) - { - c = _nc_list_data(x); - if (c != NULL) - { - switch(c->notify_type) - { - case NOTIFY_TYPE_NONE: - break; + case NOTIFY_TYPE_PLAIN: + plain_count++; + break; - case NOTIFY_TYPE_PLAIN: - plain_count++; - break; + case NOTIFY_TYPE_MEMORY: + mem_count++; + break; - case NOTIFY_TYPE_MEMORY: - mem_count++; - break; + case NOTIFY_TYPE_PORT: + port_count++; + break; - case NOTIFY_TYPE_PORT: - port_count++; - if (c->port == common_port) com_port_count++; - break; + case NOTIFY_TYPE_FILE: + file_count++; + break; - case NOTIFY_TYPE_FILE: - file_count++; - break; + case NOTIFY_TYPE_SIGNAL: + sig_count++; + break; - case NOTIFY_TYPE_SIGNAL: - sig_count++; - break; - - default: break; - } + default: + break; } } fprintf(f, "pid: %u ", pid); - if (file_count + sig_count == 0) - { - if (port_count == 0) fprintf(f, "regenerable / polling\n"); - else if (port_count == com_port_count) fprintf(f, "regenerable\n"); - else if (com_port_count > 0) fprintf(f, "partially regenerable\n"); - else fprintf(f, "non-regenerable\n"); - } - else - { - if (com_port_count == 0) fprintf(f, "non-regenerable\n"); - else fprintf(f, "partially regenerable\n"); - } - fprintf(f, "memory %u plain %u port %u file %u signal %u common port %u\n", mem_count, plain_count, port_count, file_count, sig_count, com_port_count); - for (x = l; x != NULL; x = _nc_list_next(x)) - { - c = _nc_list_data(x); - if (c != NULL) - { - fprintf(f, " %s: %s\n", notify_type_name(c->notify_type), c->name_info->name); - } + fprintf(f, "memory %u plain %u port %u file %u signal %u\n", + mem_count, plain_count, port_count, file_count, sig_count); + LIST_FOREACH(c, &pdata->clients, client_pid_entry) { + fprintf(f, " %s: %s\n", notify_type_name(c->state_and_type & NOTIFY_TYPE_MASK), c->name_info->name); } fprintf(f, "\n"); - _nc_list_release_list(l); } fprintf(f, "\n"); - _nc_table_free(pid_table); } void -dump_status(uint32_t level) +dump_status(uint32_t level, int fd) { FILE *f; - if (status_file == NULL) + if(fd < 0) { - asprintf(&status_file, "/var/run/notifyd_%u.status", getpid()); - if (status_file == NULL) return; + if (status_file == NULL) + { + asprintf(&status_file, "/var/run/notifyd_%u.status", getpid()); + if (status_file == NULL) return; + } + + unlink(status_file); + f = fopen(status_file, "w"); + } + else + { + f = fdopen(fd, "w"); } - unlink(status_file); - f = fopen(status_file, "w"); if (f == NULL) return; if (level == STATUS_REQUEST_SHORT) fprint_quick_status(f); @@ -644,6 +785,13 @@ dump_status(uint32_t level) fclose(f); } +static void +dump_status_handler(void *level) +{ + dump_status((uint32_t)level, -1); +} + + void log_message(int priority, const char *str, ...) { @@ -672,20 +820,47 @@ log_message(int priority, const char *str, ...) fclose(lfp); } +bool +has_entitlement(audit_token_t audit, const char *entitlement) +{ + xpc_object_t edata; + bool result = false; + + pid_t pid = audit_token_to_pid(audit); + + log_message(ASL_LEVEL_NOTICE, "-> has_root_entitlement (PID %d)\n", pid); + + edata = xpc_copy_entitlement_for_token(entitlement, &audit); + + if(edata != NULL){ + result = xpc_bool_get_value(edata); + xpc_release(edata); + } + + log_message(ASL_LEVEL_NOTICE, "<- has_root_entitlement (PID %d) = %s\n", pid, result ? "OK" : "FAIL"); + return result; +} + +inline bool +has_root_entitlement(audit_token_t audit) +{ + return has_entitlement(audit, ROOT_ENTITLEMENT_KEY); +} + uint32_t daemon_post(const char *name, uint32_t u, uint32_t g) { name_info_t *n; uint32_t status; - if (name == NULL) return 0; + if (name == NULL) return NOTIFY_STATUS_NULL_INPUT; - n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name); - if (n == NULL) return 0; + n = _nc_table_find(&global.notify_state.name_table, name); + if (n == NULL) return NOTIFY_STATUS_OK; if (n->slot != (uint32_t)-1) global.shared_memory_base[n->slot]++; - status = _notify_lib_post(global.notify_state, name, u, g); + status = _notify_lib_post(&global.notify_state, name, u, g); return status; } @@ -695,12 +870,12 @@ daemon_post_nid(uint64_t nid, uint32_t u, uint32_t g) name_info_t *n; uint32_t status; - n = (name_info_t *)_nc_table_find_64(global.notify_state->name_id_table, nid); - if (n == NULL) return 0; + n = _nc_table_find_64(&global.notify_state.name_id_table, nid); + if (n == NULL) return NOTIFY_STATUS_OK; if (n->slot != (uint32_t)-1) global.shared_memory_base[n->slot]++; - status = _notify_lib_post_nid(global.notify_state, nid, u, g); + status = _notify_lib_post_nid(&global.notify_state, nid, u, g); return status; } @@ -709,15 +884,15 @@ daemon_post_client(uint64_t cid) { client_t *c; - c = _nc_table_find_64(global.notify_state->client_table, cid); + c = _nc_table_find_64(&global.notify_state.client_table, cid); if (c == NULL) return; - if ((c->notify_type == NOTIFY_TYPE_MEMORY) && (c->name_info != NULL) && (c->name_info->slot != (uint32_t)-1)) + if ((c->state_and_type & NOTIFY_TYPE_MEMORY) && (c->name_info != NULL) && (c->name_info->slot != (uint32_t)-1)) { global.shared_memory_base[c->name_info->slot]++; } - _notify_lib_post_client(global.notify_state, c); + _notify_lib_post_client(&global.notify_state, c); } void @@ -727,7 +902,7 @@ daemon_set_state(const char *name, uint64_t val) if (name == NULL) return; - n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name); + n = _nc_table_find(&global.notify_state.name_table, name); if (n == NULL) return; n->state = val; @@ -736,38 +911,28 @@ daemon_set_state(const char *name, uint64_t val) static void init_launch_config(const char *name) { - launch_data_t tmp, pdict; + kern_return_t kr; - tmp = launch_data_new_string(LAUNCH_KEY_CHECKIN); - global.launch_dict = launch_msg(tmp); - launch_data_free(tmp); + kr = bootstrap_check_in(bootstrap_port, name, &global.server_port); - if (global.launch_dict == NULL) + if(kr != KERN_SUCCESS) { - fprintf(stderr, "%d launchd checkin failed\n", getpid()); - exit(1); + { + fprintf(stderr, "%d bootstrap_check_in failed (%d)\n", getpid(), kr); + exit(1); + } } - tmp = launch_data_dict_lookup(global.launch_dict, LAUNCH_JOBKEY_MACHSERVICES); - if (tmp == NULL) - { - fprintf(stderr, "%d launchd lookup of LAUNCH_JOBKEY_MACHSERVICES failed\n", getpid()); - exit(1); - } + /* + * Notifyd uses its port for communication, system wide. + * Up our queue length as most of our traffic is made of simpleroutines + */ + struct mach_port_limits limits = { + .mpl_qlimit = 32, + }; - pdict = launch_data_dict_lookup(tmp, name); - if (pdict == NULL) - { - fprintf(stderr, "%d launchd lookup of name %s failed\n", getpid(), name); - exit(1); - } - - global.server_port = launch_data_get_machport(pdict); - if (global.server_port == MACH_PORT_NULL) - { - fprintf(stderr, "%d launchd lookup of server port for name %s failed\n", getpid(), name); - exit(1); - } + mach_port_set_attributes(mach_task_self(), global.server_port, + MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, sizeof(limits)); } static void @@ -790,7 +955,7 @@ string_insert(char *s, char **l, unsigned int x) int i, len; if (s == NULL) return l; - if (l == NULL) + if (l == NULL) { l = (char **)malloc(2 * sizeof(char *)); l[0] = strdup(s); @@ -896,13 +1061,13 @@ atoaccess(char *s) } static void -init_config() +init_config(void) { FILE *f; struct stat sb; char line[1024]; char **args; - uint32_t status, argslen; + uint32_t argslen; uint32_t uid, gid, access; uint64_t nid, val64; @@ -913,8 +1078,8 @@ init_config() val64 <<= 32; val64 |= NOTIFY_IPC_VERSION; - _notify_lib_register_plain(global.notify_state, NOTIFY_IPC_VERSION_NAME, -1, notifyd_token++, -1, 0, 0, &nid); - _notify_lib_set_state(global.notify_state, nid, val64, 0, 0); + _notify_lib_register_plain(&global.notify_state, NOTIFY_IPC_VERSION_NAME, -1, notifyd_token++, -1, 0, 0, &nid); + _notify_lib_set_state(&global.notify_state, nid, val64, 0, 0); /* Check config file */ if (stat(CONFIG_FILE_PATH, &sb) != 0) return; @@ -946,44 +1111,46 @@ init_config() argslen = string_list_length(args); if (argslen == 0) continue; - if (!strcasecmp(args[0], "monitor")) - { + if (!strcasecmp(args[0], "monitor")) { if (argslen < 3) { string_list_free(args); continue; } - _notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid); - service_open_path(args[1], args[2], 0, 0); - } + _notify_lib_register_plain(&global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid); - if (!strcasecmp(args[0], "timer")) - { + dispatch_async(global.workloop, ^{ + service_open_path(args[1], args[2], 0, 0); + string_list_free(args); + }); + } else if (!strcasecmp(args[0], "timer")) { if (argslen < 3) { string_list_free(args); continue; } - _notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid); - status = service_open_timer(args[1], args[2]); - } + _notify_lib_register_plain(&global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid); - else if (!strcasecmp(args[0], "set")) - { - if (argslen < 3) + dispatch_async(global.workloop, ^{ + service_open_timer(args[1], args[2]); + string_list_free(args); + }); + } else if (!strcasecmp(args[0], "set")) { + if (argslen == 1 || argslen > 3) { string_list_free(args); continue; } - val64 = atoll(args[2]); + _notify_lib_register_plain(&global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid); + if (argslen == 3) + { + val64 = atoll(args[2]); + _notify_lib_set_state(&global.notify_state, nid, val64, 0, 0); + } - _notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid); - _notify_lib_set_state(global.notify_state, nid, val64, 0, 0); - } - - else if (!strcasecmp(args[0], "reserve")) - { + string_list_free(args); + } else if (!strcasecmp(args[0], "reserve")) { if (argslen == 1) { string_list_free(args); @@ -998,78 +1165,70 @@ init_config() if (argslen > 3) gid = atoi(args[3]); if (argslen > 4) access = atoaccess(args[4]); - if ((uid != 0) || (gid != 0)) _notify_lib_set_owner(global.notify_state, args[1], uid, gid); - if (access != NOTIFY_ACCESS_DEFAULT) _notify_lib_set_access(global.notify_state, args[1], access); - } - else if (!strcasecmp(args[0], "quit")) - { + if ((uid != 0) || (gid != 0)) _notify_lib_set_owner(&global.notify_state, args[1], uid, gid); + if (access != NOTIFY_ACCESS_DEFAULT) _notify_lib_set_access(&global.notify_state, args[1], access); + + string_list_free(args); + } else if (!strcasecmp(args[0], "quit")) { string_list_free(args); break; } - string_list_free(args); } fclose(f); } static void -service_mach_message(bool blocking) +notifyd_mach_channel_handler(void *context, dispatch_mach_reason_t reason, + dispatch_mach_msg_t message, mach_error_t error) { - __block kern_return_t status; - uint32_t rbits, sbits; - notify_request_msg *request; - notify_reply_msg *reply; - char rbuf[sizeof(notify_request_msg) + MAX_TRAILER_SIZE]; - char sbuf[sizeof(notify_reply_msg) + MAX_TRAILER_SIZE]; - - forever - { - memset(rbuf, 0, sizeof(rbuf)); - memset(sbuf, 0, sizeof(sbuf)); - - request = (notify_request_msg *)rbuf; - reply = (notify_reply_msg *)sbuf; - - request->head.msgh_local_port = global.server_port; - request->head.msgh_size = global.request_size; - - rbits = MACH_RCV_MSG | (blocking ? 0 : MACH_RCV_TIMEOUT) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | MACH_RCV_VOUCHER; - sbits = MACH_SEND_MSG; - - status = mach_msg(&(request->head), rbits, 0, global.request_size, global.server_port, 0, MACH_PORT_NULL); - if (status != KERN_SUCCESS) return; - - voucher_mach_msg_state_t voucher = voucher_mach_msg_adopt(&(request->head)); - -#if TARGET_OS_EMBEDDED - /* Synchronize with work_q since on embedded main() calls this - * from the global concurrent queue. */ - dispatch_sync(global.work_q, ^{ - status = notify_ipc_server(&(request->head), &(reply->head)); - }); -#else - status = notify_ipc_server(&(request->head), &(reply->head)); -#endif - - if (!status && (request->head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) - { - /* destroy the request - but not the reply port */ - request->head.msgh_remote_port = MACH_PORT_NULL; - mach_msg_destroy(&(request->head)); + static const struct mig_subsystem *subsystems[] = { + (mig_subsystem_t)&_notify_ipc_subsystem, + }; + if (reason == DISPATCH_MACH_MESSAGE_RECEIVED) { + if (!dispatch_mach_mig_demux(context, subsystems, 1, message)) { + mach_msg_destroy(dispatch_mach_msg_get_msg(message, NULL)); } + } +} - if (reply->head.msgh_remote_port) - { - status = mach_msg(&(reply->head), sbits, reply->head.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); - if (status == MACH_SEND_INVALID_DEST || status == MACH_SEND_TIMED_OUT) - { - /* deallocate reply port rights not consumed by failed mach_msg() send */ - mach_msg_destroy(&(reply->head)); - } +kern_return_t +do_mach_notify_port_deleted(mach_port_t notify, mach_port_name_t name) +{ + NOTIFY_INTERNAL_CRASH(0, "notifyd never requests port-deleted"); +} + +kern_return_t +do_mach_notify_port_destroyed(mach_port_t notify, mach_port_t rights) +{ + NOTIFY_INTERNAL_CRASH(0, "notifyd never requests port-destroyed"); +} + +kern_return_t +do_mach_notify_no_senders(mach_port_t notify, mach_port_mscount_t mscount) +{ + NOTIFY_INTERNAL_CRASH(0, "notifyd never requests no-senders"); +} + +kern_return_t +do_mach_notify_send_once(mach_port_t notify) +{ + NOTIFY_INTERNAL_CRASH(0, "someone destroyed our send-possible notification"); +} + +static void +mach_notifs_handle(void *context, dispatch_mach_reason_t reason, + dispatch_mach_msg_t msg, mach_error_t error) +{ + static const struct mig_subsystem *const subsystems[] = { + (mig_subsystem_t)&do_notify_subsystem, + }; + + if (reason == DISPATCH_MACH_MESSAGE_RECEIVED) { + if (!dispatch_mach_mig_demux(context, subsystems, 1, msg)) { + mach_msg_destroy(dispatch_mach_msg_get_msg(msg, NULL)); } - - voucher_mach_msg_revert(voucher); } } @@ -1129,15 +1288,15 @@ open_shared_memory(const char *name) int main(int argc, const char *argv[]) { - dispatch_queue_t main_q; const char *service_name; const char *shm_name; uint32_t i, status; struct rlimit rlim; + kern_return_t kr; -#if TARGET_IPHONE_SIMULATOR +#if TARGET_OS_SIMULATOR asprintf(&_config_file_path, "%s/private/etc/notify.conf", getenv("SIMULATOR_ROOT")); - asprintf(&_debug_log_path, "%s/var/log/notifyd.log", getenv("SIMULATOR_LOG_ROOT")); + asprintf(&_debug_log_path, "%s/var/log/notifyd.log", getenv("SIMULATOR_LOG_ROOT")); #endif service_name = NOTIFY_SERVICE_NAME; @@ -1158,10 +1317,9 @@ main(int argc, const char *argv[]) memset(&call_statistics, 0, sizeof(struct call_statistics_s)); - global.request_size = sizeof(notify_request_msg) + MAX_TRAILER_SIZE; - global.reply_size = sizeof(notify_reply_msg) + MAX_TRAILER_SIZE; global.nslots = getpagesize() / sizeof(uint32_t); - global.notify_state = _notify_lib_notify_state_new(NOTIFY_STATE_ENABLE_RESEND, 1024); + _notify_lib_notify_state_init(&global.notify_state, NOTIFY_STATE_ENABLE_RESEND); + global.log_cutoff = ASL_LEVEL_ERR; global.log_path = strdup(DEBUG_LOG_PATH); global.slot_id = (uint32_t)-1; @@ -1207,59 +1365,72 @@ main(int argc, const char *argv[]) assert(status == 0); } - /* init from config file before starting the work queue */ + global.workloop = dispatch_workloop_create_inactive("com.apple.notifyd.main"); + dispatch_set_qos_class_fallback(global.workloop, QOS_CLASS_UTILITY); + dispatch_activate(global.workloop); + + /* init from config file before starting the listener */ init_config(); - main_q = dispatch_get_main_queue(); - assert(main_q != NULL); + mach_port_options_t opts = { + .flags = MPO_STRICT | MPO_CONTEXT_AS_GUARD, + }; + kr = mach_port_construct(current_task(), &opts, &global, &global.mach_notify_port); + if (kr != KERN_SUCCESS) { + NOTIFY_INTERNAL_CRASH(kr, "Unable to allocate Mach notification port"); + } + global.mach_notifs_channel = dispatch_mach_create_f("com.apple.notifyd.mach-notifs", + global.workloop, NULL, mach_notifs_handle); + dispatch_set_qos_class_fallback(global.mach_notifs_channel, QOS_CLASS_USER_INITIATED); + dispatch_mach_connect(global.mach_notifs_channel, global.mach_notify_port, + MACH_PORT_NULL, NULL); - global.work_q = dispatch_queue_create("WorkQ", NULL); - assert(global.work_q != NULL); - -#if TARGET_OS_EMBEDDED - /* Block a thread in mach_msg() to avoid the syscall overhead of frequent - * dispatch source wakeup, and synchronize with work_q after message - * reception in service_mach_message(). */ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ - forever service_mach_message(true); - }); + global.mach_channel = dispatch_mach_create_f("com.apple.notifyd.channel", + global.workloop, NULL, notifyd_mach_channel_handler); +#if TARGET_OS_SIMULATOR + // simulators don't support MiG QoS propagation yet + dispatch_set_qos_class_fallback(global.mach_channel, QOS_CLASS_USER_INITIATED); #else - global.mach_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, global.server_port, 0, global.work_q); - assert(global.mach_src != NULL); - - dispatch_source_set_event_handler(global.mach_src, ^{ - service_mach_message(false); - }); - dispatch_resume(global.mach_src); + dispatch_set_qos_class_fallback(global.mach_channel, QOS_CLASS_BACKGROUND); #endif + dispatch_mach_connect(global.mach_channel, global.server_port, MACH_PORT_NULL, NULL); /* Set up SIGUSR1 */ - global.sig_usr1_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGUSR1, 0, main_q); + global.sig_usr1_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, + (uintptr_t)SIGUSR1, 0, global.workloop); assert(global.sig_usr1_src != NULL); - dispatch_source_set_event_handler(global.sig_usr1_src, ^{ - dispatch_async(global.work_q, ^{ dump_status(STATUS_REQUEST_SHORT); }); - }); - dispatch_resume(global.sig_usr1_src); + dispatch_set_context(global.sig_usr1_src, (void *)STATUS_REQUEST_SHORT); + dispatch_source_set_event_handler_f(global.sig_usr1_src, dump_status_handler); + dispatch_activate(global.sig_usr1_src); /* Set up SIGUSR2 */ - global.sig_usr2_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGUSR2, 0, main_q); + global.sig_usr2_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, + (uintptr_t)SIGUSR2, 0, global.workloop); assert(global.sig_usr2_src != NULL); - dispatch_source_set_event_handler(global.sig_usr2_src, ^{ - dispatch_async(global.work_q, ^{ dump_status(STATUS_REQUEST_LONG); }); - }); - dispatch_resume(global.sig_usr2_src); + dispatch_set_context(global.sig_usr2_src, (void *)STATUS_REQUEST_LONG); + dispatch_source_set_event_handler_f(global.sig_usr2_src, dump_status_handler); + dispatch_activate(global.sig_usr2_src); /* Set up SIGWINCH */ - global.sig_winch_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGWINCH, 0, main_q); + global.sig_winch_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, + (uintptr_t)SIGWINCH, 0, global.workloop); assert(global.sig_winch_src != NULL); dispatch_source_set_event_handler(global.sig_winch_src, ^{ if (global.log_cutoff == ASL_LEVEL_DEBUG) global.log_cutoff = global.log_default; else global.log_cutoff = ASL_LEVEL_DEBUG; }); - dispatch_resume(global.sig_winch_src); + dispatch_activate(global.sig_winch_src); + + global.stat_reset_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, global.workloop); + { + dispatch_time_t trigger = dispatch_walltime(NULL, 0); + dispatch_source_set_timer(global.stat_reset_src, trigger, 60 * 60 * NSEC_PER_SEC, 0); + } + dispatch_source_set_event_handler(global.stat_reset_src, ^{ + notify_reset_stats(); + }); + dispatch_activate(global.stat_reset_src); + notify_reset_stats(); dispatch_main(); - - /* NOTREACHED */ - return 0; } diff --git a/src/libnotify/notifyd/notifyd.h b/src/libnotify/notifyd/notifyd.h index a32856036..04385f046 100644 --- a/src/libnotify/notifyd/notifyd.h +++ b/src/libnotify/notifyd/notifyd.h @@ -24,25 +24,35 @@ #ifndef _NOTIFY_DAEMON_H_ #define _NOTIFY_DAEMON_H_ +#define DISPATCH_MACH_SPI 1 + #include #include #include #include +#include + + +#define STATUS_REQUEST_SHORT 0 +#define STATUS_REQUEST_LONG 1 + +#define NOTIFY_STATE_ENTITLEMENT "com.apple.private.libnotify.statecapture" -#define NOTIFY_IPC_VERSION 2 struct global_s { + notify_state_t notify_state; + dispatch_mach_t mach_notifs_channel; + mach_port_t mach_notify_port; mach_port_t server_port; - launch_data_t launch_dict; - notify_state_t *notify_state; - dispatch_queue_t work_q; - dispatch_source_t mach_src; + void **service_info_list; + dispatch_workloop_t workloop; + dispatch_mach_t mach_channel; dispatch_source_t sig_usr1_src; dispatch_source_t sig_usr2_src; dispatch_source_t sig_winch_src; - uint32_t request_size; - uint32_t reply_size; + dispatch_source_t stat_reset_src; + time_t last_reset_time; uint32_t nslots; uint32_t slot_id; uint32_t *shared_memory_base; @@ -50,8 +60,11 @@ struct global_s uint32_t *last_shm_base; uint32_t log_cutoff; uint32_t log_default; + uint16_t service_info_count; char *log_path; -} global; +}; + +extern struct global_s global; struct call_statistics_s { @@ -80,22 +93,27 @@ struct call_statistics_s uint64_t set_state_by_client; uint64_t set_state_by_id; uint64_t set_state_by_client_and_fetch_id; - uint64_t get_owner; uint64_t set_owner; - uint64_t get_access; uint64_t set_access; uint64_t monitor_file; uint64_t service_timer; uint64_t service_path; uint64_t cleanup; uint64_t regenerate; -} call_statistics; + uint64_t checkin; +}; -extern void log_message(int priority, const char *str, ...); +extern struct call_statistics_s call_statistics; + +extern void log_message(int priority, const char *str, ...) __printflike(2, 3); extern uint32_t daemon_post(const char *name, uint32_t u, uint32_t g); extern uint32_t daemon_post_nid(uint64_t nid, uint32_t u, uint32_t g); extern void daemon_post_client(uint64_t cid); extern void daemon_set_state(const char *name, uint64_t val); -extern void dump_status(uint32_t level); +extern void dump_status(uint32_t level, int fd); +extern bool has_entitlement(audit_token_t audit, const char *entitlement); +extern bool has_root_entitlement(audit_token_t audit); + +dispatch_queue_t get_notifyd_workloop(void); #endif /* _NOTIFY_DAEMON_H_ */ diff --git a/src/libnotify/notifyd/pathwatch.c b/src/libnotify/notifyd/pathwatch.c index a6a615756..e45853dd7 100644 --- a/src/libnotify/notifyd/pathwatch.c +++ b/src/libnotify/notifyd/pathwatch.c @@ -55,6 +55,10 @@ * * path_node_releases() releases a path_node_t object and all of the vnode_t objects * that were monitoring components of its target path. + * + * All of the code in this file is to be run on the workloop in order to maintain internal + * datastructures. This is asserted in every non-static function and thus can be safely + * assumed by all static functions. */ #include @@ -69,7 +73,10 @@ #include #include #include +#include +#include #include "pathwatch.h" +#include "notifyd.h" #define forever for(;;) #define streq(A,B) (strcmp(A,B)==0) @@ -110,7 +117,6 @@ typedef struct static struct { dispatch_once_t pathwatch_init; - dispatch_queue_t pathwatch_queue; uint32_t vnode_count; vnode_t **vnode; char *tzdir; @@ -206,7 +212,7 @@ _path_stat(const char *path, int link, uid_t uid, gid_t gid) * Sets ftype output parameter if it is non-NULL. */ static int -_path_stat_check_access(const char *path, uid_t uid, gid_t gid, uint32_t *ftype) +_path_stat_check_access(const char *path, audit_token_t audit, bool client_is_notifyd, uint32_t *ftype) { struct stat sb; char buf[MAXPATHLEN + 1]; @@ -226,6 +232,11 @@ _path_stat_check_access(const char *path, uid_t uid, gid_t gid, uint32_t *ftype) return PATH_STAT_OK; } + /* Don't perform stat if sandbox won't allow it. (15907527) */ + if (!client_is_notifyd && (sandbox_check_by_audit_token(audit, "file-read-metadata", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, path) != 0)) { + return PATH_STAT_ACCESS; + } + memset(&sb, 0, sizeof(struct stat)); status = lstat(path, &sb); @@ -239,8 +250,8 @@ _path_stat_check_access(const char *path, uid_t uid, gid_t gid, uint32_t *ftype) if (t == PATH_NODE_TYPE_OTHER) return PATH_STAT_FAILED; - /* skip access control check if uid is zero */ - if (uid == 0) return 0; + /* skip access control check if uid is zero or if the client is notifyd */ + if (client_is_notifyd || audit_token_to_euid(audit) == 0) return 0; /* special case: anything in the timezone directory is OK */ memset(buf, 0, sizeof(buf)); @@ -253,20 +264,20 @@ _path_stat_check_access(const char *path, uid_t uid, gid_t gid, uint32_t *ftype) /* call _path_stat to check access as the user/group provided */ if (t == PATH_NODE_TYPE_FILE) { - status = _path_stat(path, 0, uid, gid); + status = _path_stat(path, 0, audit_token_to_euid(audit), audit_token_to_egid(audit)); if ((status == PATH_STAT_ACCESS) && (ftype != NULL)) *ftype = PATH_NODE_TYPE_GHOST; return status; } else if (t == PATH_NODE_TYPE_LINK) { - status = _path_stat(path, 1, uid, gid); + status = _path_stat(path, 1, audit_token_to_euid(audit), audit_token_to_egid(audit)); if ((status == PATH_STAT_ACCESS) && (ftype != NULL)) *ftype = PATH_NODE_TYPE_GHOST; return status; } else if (t == PATH_NODE_TYPE_DIR) { snprintf(buf, MAXPATHLEN, "%s/.", path); - status = _path_stat(buf, 0, uid, gid); + status = _path_stat(buf, 0, audit_token_to_euid(audit), audit_token_to_egid(audit)); if ((status == PATH_STAT_ACCESS) && (ftype != NULL)) *ftype = PATH_NODE_TYPE_GHOST; return status; } @@ -319,12 +330,7 @@ _vnode_free(vnode_t *vnode) { dispatch_source_cancel(vnode->src); - /* - * Actually free the vnode on the pathwatch queue. This allows any - * enqueued _vnode_event operations to complete before the vnode disappears. - * _vnode_event() quietly returns if the source has been cancelled. - */ - dispatch_async(_global.pathwatch_queue, ^{ + dispatch_async(get_notifyd_workloop(), ^{ dispatch_release(vnode->src); free(vnode->path); free(vnode->path_node); @@ -347,7 +353,7 @@ _vnode_event(vnode_t *vnode) if ((vnode->src != NULL) && (dispatch_source_testcancel(vnode->src))) return; ulf = dispatch_source_get_data(vnode->src); - flags = ulf; + flags = (uint32_t)ulf; memset(&sb, 0, sizeof(struct stat)); if (fstat(vnode->fd, &sb) == 0) @@ -414,7 +420,7 @@ _vnode_create(const char *path, uint32_t type, path_node_t *pnode) fd = open(path, flags, 0); if (fd < 0) return NULL; - src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, (uintptr_t)fd, DISPATCH_VNODE_ALL, _global.pathwatch_queue); + src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, (uintptr_t)fd, DISPATCH_VNODE_ALL, get_notifyd_workloop()); if (src == NULL) { close(fd); @@ -596,7 +602,6 @@ _vnode_release_for_node(path_node_t *pnode) /* * Retain a path_node_t object. - * Dispatched on _global.pathwatch_queue. */ static void _path_node_retain(path_node_t *pnode) @@ -607,7 +612,6 @@ _path_node_retain(path_node_t *pnode) /* * Free a path_node_t object. - * Dispatched on _global.pathwatch_queue. */ static void _path_node_free(path_node_t *pnode) @@ -641,7 +645,6 @@ _path_node_free(path_node_t *pnode) free(pnode->contextp); dispatch_release(pnode->src); - dispatch_release(pnode->src_queue); memset(pnode, 0, sizeof(path_node_t)); free(pnode); @@ -655,28 +658,19 @@ _path_node_release(path_node_t *pnode) { if (pnode == NULL) return; - /* - * We need to make sure that the node's event handler isn't currently - * executing before freeing the node. We dispatch on the src_queue, so - * that when the block executes there will be no more events in the queue. - * From there, we dispatch async back to the pathwatch_queue to do the - * data structure cleanup. - */ - dispatch_async(pnode->src_queue, ^{ - dispatch_async(_global.pathwatch_queue, ^{ - if (pnode->refcount > 0) pnode->refcount--; - if (pnode->refcount == 0) _path_node_free(pnode); - }); - }); + + if (pnode->refcount > 0) pnode->refcount--; + if (pnode->refcount == 0) _path_node_free(pnode); } /* * Frees a path_node_t object. - * The work is actually done on the global pathwatch_queue to make this safe. */ void path_node_close(path_node_t *pnode) { + dispatch_assert_queue(get_notifyd_workloop()); + if (pnode == NULL) return; if (pnode->src != NULL) dispatch_source_cancel(pnode->src); @@ -687,9 +681,6 @@ static void _pathwatch_init() { char buf[MAXPATHLEN]; - - /* Create serial queue for node creation / deletion operations */ - _global.pathwatch_queue = dispatch_queue_create("pathwatch", NULL); _global.tzdir = NULL; _global.tzdir_len = 0; @@ -795,7 +786,6 @@ _path_node_init(const char *path) return pnode; } -/* dispatched on _global.pathwatch_queue */ static void _path_node_update(path_node_t *pnode, uint32_t flags, vnode_t *vnode) { @@ -810,7 +800,7 @@ _path_node_update(path_node_t *pnode, uint32_t flags, vnode_t *vnode) old_type = pnode->type; - status = _path_stat_check_access(pnode->path, pnode->uid, pnode->gid, &(pnode->type)); + status = _path_stat_check_access(pnode->path, pnode->audit, pnode->flags & PATH_NODE_CLIENT_NOTIFYD, &(pnode->type)); if (status == PATH_STAT_ACCESS) flags |= DISPATCH_VNODE_REVOKE; data = 0; @@ -862,7 +852,7 @@ _path_node_update(path_node_t *pnode, uint32_t flags, vnode_t *vnode) dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, PNODE_COALESCE_TIME); _path_node_retain(pnode); - dispatch_after(delay, _global.pathwatch_queue, ^{ + dispatch_after(delay, get_notifyd_workloop(), ^{ pnode->flags &= ~PATH_SRC_SUSPENDED; dispatch_resume(pnode->src); _path_node_release(pnode); @@ -874,7 +864,7 @@ _path_node_update(path_node_t *pnode, uint32_t flags, vnode_t *vnode) } buf = NULL; - if (pnode->plen < MAXPATHLEN) buf = fixed; + if (pnode->plen > MAXPATHLEN) buf = fixed; else buf = malloc(pnode->plen); assert(buf != NULL); @@ -928,9 +918,12 @@ _path_node_update(path_node_t *pnode, uint32_t flags, vnode_t *vnode) * be shared with other path_node_t structures. */ path_node_t * -path_node_create(const char *path, uid_t uid, gid_t gid, uint32_t mask, dispatch_queue_t queue) +path_node_create(const char *path, audit_token_t audit, bool is_notifyd, uint32_t mask) { path_node_t *pnode; + dispatch_queue_t queue = get_notifyd_workloop(); + + dispatch_assert_queue(queue); dispatch_once(&(_global.pathwatch_init), ^{ _pathwatch_init(); }); @@ -938,16 +931,17 @@ path_node_create(const char *path, uid_t uid, gid_t gid, uint32_t mask, dispatch if (pnode == NULL) return NULL; pnode->refcount = 1; - pnode->uid = uid; - pnode->gid = gid; + pnode->audit = audit; - dispatch_sync(_global.pathwatch_queue, ^{ _path_node_update(pnode, 0, NULL); }); + _path_node_update(pnode, 0, NULL); dispatch_retain(queue); pnode->src = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_OR, 0, 0, queue); - pnode->src_queue = queue; pnode->flags = mask & PATH_NODE_ALL; - + if (is_notifyd) + { + pnode->flags |= PATH_NODE_CLIENT_NOTIFYD; + } return pnode; } diff --git a/src/libnotify/notifyd/pathwatch.h b/src/libnotify/notifyd/pathwatch.h index ab0eb26cf..637225e85 100644 --- a/src/libnotify/notifyd/pathwatch.h +++ b/src/libnotify/notifyd/pathwatch.h @@ -53,6 +53,8 @@ enum #define PATH_NODE_ALL 0x000003ff /* src is suspended */ #define PATH_SRC_SUSPENDED 0x10000000 +/* the client is notifyd */ +#define PATH_NODE_CLIENT_NOTIFYD 0x20000000 /* Path changes coalesce for 100 milliseconds */ #define PNODE_COALESCE_TIME 100000000 @@ -64,21 +66,19 @@ typedef struct { char *path; size_t plen; - uid_t uid; - gid_t gid; + audit_token_t audit; uint32_t pname_count; char **pname; uint32_t type; uint32_t flags; dispatch_source_t src; - dispatch_queue_t src_queue; void *contextp; uint32_t context32; uint64_t context64; uint32_t refcount; } path_node_t; -path_node_t *path_node_create(const char *path, uid_t uid, gid_t gid, uint32_t mask, dispatch_queue_t queue); +path_node_t *path_node_create(const char *path, audit_token_t audit, bool is_notifyd, uint32_t mask); void path_node_close(path_node_t *pnode); #endif /* _PATHWATCH_H_ */ diff --git a/src/libnotify/notifyd/service.c b/src/libnotify/notifyd/service.c index 1fef06576..2e2b6c0ed 100644 --- a/src/libnotify/notifyd/service.c +++ b/src/libnotify/notifyd/service.c @@ -32,6 +32,7 @@ #include "service.h" #include "pathwatch.h" #include "timer.h" +#include "notify_internal.h" #define NOTIFY_PATH_SERVICE "path:" #define NOTIFY_PATH_SERVICE_LEN 5 @@ -81,7 +82,7 @@ service_open_path(const char *name, const char *path, uid_t uid, gid_t gid) if (path == NULL) return NOTIFY_STATUS_INVALID_REQUEST; - n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name); + n = _nc_table_find(&global.notify_state.name_table, name); if (n == NULL) return NOTIFY_STATUS_INVALID_NAME; if (n->private != NULL) @@ -98,8 +99,13 @@ service_open_path(const char *name, const char *path, uid_t uid, gid_t gid) return NOTIFY_STATUS_OK; } - node = path_node_create(path, uid, gid, PATH_NODE_ALL, dispatch_get_main_queue()); - if (node == NULL) return NOTIFY_STATUS_FAILED; + { + audit_token_t audit; + memset(&audit, 0, sizeof(audit_token_t)); + node = path_node_create(path, audit, true, PATH_NODE_ALL); + } + + if (node == NULL) return NOTIFY_STATUS_PATH_NODE_CREATE_FAILED; node->contextp = strdup(name); @@ -111,24 +117,72 @@ service_open_path(const char *name, const char *path, uid_t uid, gid_t gid) n->private = info; dispatch_source_set_event_handler(node->src, ^{ - dispatch_async(global.work_q, ^{ - if (0 == dispatch_source_testcancel(node->src)) - { - daemon_post((const char *)node->contextp, uid, gid); - } - }); + daemon_post((const char *)node->contextp, uid, gid); }); - dispatch_resume(node->src); + dispatch_activate(node->src); return NOTIFY_STATUS_OK; } +static uint16_t service_info_add(void *info) +{ + assert(global.service_info_count != UINT16_MAX); + + for(int i = 0; i < global.service_info_count; i++) + { + if(global.service_info_list[i] == NULL){ + global.service_info_list[i] = info; + return i + 1; + } + } + + if(global.service_info_count == 0){ + global.service_info_count = 1; + global.service_info_list = malloc(sizeof(void *)); + } else { + global.service_info_count++; + global.service_info_list = realloc(global.service_info_list, global.service_info_count * sizeof(void *)); + } + + global.service_info_list[global.service_info_count - 1] = info; + + return global.service_info_count; +} + +void *service_info_get(uint16_t index) +{ + if(index == 0) + { + return NULL; + } + + return global.service_info_list[index - 1]; +} + + +static void *service_info_remove(uint16_t index) +{ + if(index == 0) + { + return NULL; + } + + void *ret = global.service_info_list[index - 1]; + + global.service_info_list[index - 1] = NULL; + + return ret; + +} + + /* * The private (per-client) path watch service. + * Must be callled on global.workloop if it is initialized */ int -service_open_path_private(const char *name, client_t *c, const char *path, uid_t uid, gid_t gid, uint32_t flags) +service_open_path_private(const char *name, client_t *c, const char *path, audit_token_t audit, uint32_t flags) { name_info_t *n; svc_info_t *info; @@ -138,14 +192,14 @@ service_open_path_private(const char *name, client_t *c, const char *path, uid_t if (path == NULL) return NOTIFY_STATUS_INVALID_REQUEST; - n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name); + n = _nc_table_find(&global.notify_state.name_table, name); if (n == NULL) return NOTIFY_STATUS_INVALID_NAME; - if (c == NULL) return NOTIFY_STATUS_FAILED; + if (c == NULL) return NOTIFY_STATUS_NULL_INPUT; - if (c->private != NULL) + if (c->service_index != 0) { /* a client may only have one service */ - info = (svc_info_t *)c->private; + info = (svc_info_t *)service_info_get(c->service_index); if (info->type != SERVICE_TYPE_PATH_PRIVATE) return NOTIFY_STATUS_INVALID_REQUEST; /* the client must be asking for the same path that is being monitored */ @@ -158,28 +212,23 @@ service_open_path_private(const char *name, client_t *c, const char *path, uid_t if (flags == 0) flags = PATH_NODE_ALL; - node = path_node_create(path, uid, gid, flags, dispatch_get_main_queue()); - if (node == NULL) return NOTIFY_STATUS_FAILED; + node = path_node_create(path, audit, false, flags); + if (node == NULL) return NOTIFY_STATUS_PATH_NODE_CREATE_FAILED; - node->context64 = c->client_id; + node->context64 = c->cid.hash_key; info = (svc_info_t *)calloc(1, sizeof(svc_info_t)); assert(info != NULL); info->type = SERVICE_TYPE_PATH_PRIVATE; info->private = node; - c->private = info; + c->service_index = service_info_add(info); dispatch_source_set_event_handler(node->src, ^{ - dispatch_async(global.work_q, ^{ - if (0 == dispatch_source_testcancel(node->src)) - { - daemon_post_client(node->context64); - } - }); + daemon_post_client(node->context64); }); - dispatch_resume(node->src); + dispatch_activate(node->src); return NOTIFY_STATUS_OK; } @@ -296,7 +345,7 @@ service_open_timer(const char *name, const char *args) call_statistics.service_timer++; - n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name); + n = _nc_table_find(&global.notify_state.name_table, name); if (n == NULL) return NOTIFY_STATUS_INVALID_NAME; s = f = e = 0; @@ -323,26 +372,26 @@ service_open_timer(const char *name, const char *args) { case TIME_EVENT_ONESHOT: { - timer = timer_oneshot(s, dispatch_get_main_queue()); + timer = timer_oneshot(s, global.workloop); break; } case TIME_EVENT_CLOCK: { - timer = timer_clock(s, f, e, dispatch_get_main_queue()); + timer = timer_clock(s, f, e, global.workloop); break; } case TIME_EVENT_CAL: { - timer = timer_calendar(s, f, d, e, dispatch_get_main_queue()); + timer = timer_calendar(s, f, e, d, global.workloop); break; } default: { - return NOTIFY_STATUS_FAILED; + return NOTIFY_STATUS_INVALID_TIME_EVENT; } } - if (timer == NULL) return NOTIFY_STATUS_FAILED; + if (timer == NULL) return NOTIFY_STATUS_TIMER_FAILED; timer->contextp = strdup(name); info = (svc_info_t *)calloc(1, sizeof(svc_info_t)); @@ -353,15 +402,10 @@ service_open_timer(const char *name, const char *args) n->private = info; dispatch_source_set_event_handler(timer->src, ^{ - dispatch_async(global.work_q, ^{ - if (0 == dispatch_source_testcancel(timer->src)) - { - daemon_post((const char *)timer->contextp, 0, 0); - } - }); + daemon_post((const char *)timer->contextp, 0, 0); }); - - dispatch_resume(timer->src); + + dispatch_activate(timer->src); return NOTIFY_STATUS_OK; } @@ -378,9 +422,9 @@ service_open_timer_private(const char *name, client_t *c, const char *args) call_statistics.service_timer++; - n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name); + n = _nc_table_find(&global.notify_state.name_table, name); if (n == NULL) return NOTIFY_STATUS_INVALID_NAME; - if (c == NULL) return NOTIFY_STATUS_FAILED; + if (c == NULL) return NOTIFY_STATUS_NULL_INPUT; s = f = e = 0; d = 0; @@ -388,10 +432,10 @@ service_open_timer_private(const char *name, client_t *c, const char *args) t = parse_timer_args(args, &s, &f, &e, &d); if (t == TIME_EVENT_NONE) return NOTIFY_STATUS_INVALID_REQUEST; - if (c->private != NULL) + if (c->service_index != 0) { /* a client may only have one service */ - info = (svc_info_t *)c->private; + info = (svc_info_t *)service_info_get(c->service_index); if (info->type != SERVICE_TYPE_TIMER_PRIVATE) return NOTIFY_STATUS_INVALID_REQUEST; /* the client must be asking for the same timer that is active */ @@ -406,52 +450,47 @@ service_open_timer_private(const char *name, client_t *c, const char *args) { case TIME_EVENT_ONESHOT: { - timer = timer_oneshot(s, dispatch_get_main_queue()); + timer = timer_oneshot(s, global.workloop); break; } case TIME_EVENT_CLOCK: { - timer = timer_clock(s, f, e, dispatch_get_main_queue()); + timer = timer_clock(s, f, e, global.workloop); break; } case TIME_EVENT_CAL: { - timer = timer_calendar(s, f, d, e, dispatch_get_main_queue()); + timer = timer_calendar(s, f, e, d, global.workloop); break; } default: { - return NOTIFY_STATUS_FAILED; + return NOTIFY_STATUS_INVALID_TIME_EVENT; } } - if (timer == NULL) return NOTIFY_STATUS_FAILED; - timer->context64 = c->client_id; + if (timer == NULL) return NOTIFY_STATUS_TIMER_FAILED; + timer->context64 = c->cid.hash_key; info = (svc_info_t *)calloc(1, sizeof(svc_info_t)); assert(info != NULL); info->type = SERVICE_TYPE_TIMER_PRIVATE; info->private = timer; - c->private = info; + c->service_index = service_info_add(info); dispatch_source_set_event_handler(timer->src, ^{ - dispatch_async(global.work_q, ^{ - if (0 == dispatch_source_testcancel(timer->src)) - { - daemon_post_client(timer->context64); - } - }); + daemon_post_client(timer->context64); }); - dispatch_resume(timer->src); + dispatch_activate(timer->src); return NOTIFY_STATUS_OK; } /* called from server-side routines in notify_proc - services are private to the client */ int -service_open(const char *name, client_t *client, uint32_t uid, uint32_t gid) +service_open(const char *name, client_t *client, audit_token_t audit) { uint32_t t, flags; char *p, *q; @@ -474,11 +513,11 @@ service_open(const char *name, client_t *client, uint32_t uid, uint32_t gid) q = strchr(p, ':'); if (q != NULL) { - flags = strtol(p, NULL, 0); + flags = (uint32_t)strtol(p, NULL, 0); p = q + 1; } - return service_open_path_private(name, client, p, uid, gid, flags); + return service_open_path_private(name, client, p, audit, flags); } case SERVICE_TYPE_TIMER_PRIVATE: { @@ -496,9 +535,11 @@ service_open(const char *name, client_t *client, uint32_t uid, uint32_t gid) } void -service_close(svc_info_t *info) +service_close(uint16_t service_index) { - if (info == NULL) return; + if (service_index == 0) return; + + svc_info_t *info = service_info_remove(service_index); switch (info->type) { diff --git a/src/libnotify/notifyd/service.h b/src/libnotify/notifyd/service.h index bc27071ab..21a5e0731 100644 --- a/src/libnotify/notifyd/service.h +++ b/src/libnotify/notifyd/service.h @@ -39,11 +39,12 @@ typedef struct void *private; } svc_info_t; -int service_open(const char *name, client_t *client, uint32_t uid, uint32_t gid); +int service_open(const char *name, client_t *client, audit_token_t audit); int service_open_path(const char *name, const char *path, uid_t uid, gid_t gid); -int service_open_path_private(const char *name, client_t *client, const char *path, uid_t uid, gid_t gid, uint32_t flags); +int service_open_path_private(const char *name, client_t *client, const char *path, audit_token_t audit, uint32_t flags); int service_open_timer(const char *name, const char *args); int service_open_timer_private(const char *name, client_t *client, const char *args); -void service_close(svc_info_t *info); +void service_close(uint16_t service_index); +void *service_info_get(uint16_t index); #endif /* _NOTIFY_SERVICE_H_ */ diff --git a/src/libnotify/notifyd/table.h b/src/libnotify/notifyd/table.h deleted file mode 100644 index f7afb37f1..000000000 --- a/src/libnotify/notifyd/table.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2003-2011 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef _NOTIFY_TABLE_H_ -#define _NOTIFY_TABLE_H_ - -#include - -typedef struct __table_private table_t; -typedef struct __list_private list_t; - -extern table_t *_nc_table_new(uint32_t n); - -extern void _nc_table_insert(table_t *t, const char *key, void *datum); -extern void _nc_table_insert_no_copy(table_t *t, const char *key, void *datum); -extern void _nc_table_insert_n(table_t *t, uint32_t key, void *datum); -extern void _nc_table_insert_64(table_t *t, uint64_t key, void *datum); - -extern void *_nc_table_find(table_t *t, const char *key); -extern void *_nc_table_find_n(table_t *t, uint32_t key); -extern void *_nc_table_find_64(table_t *t, uint64_t key); - -extern void _nc_table_delete(table_t *t, const char *key); -extern void _nc_table_delete_n(table_t *t, uint32_t key); -extern void _nc_table_delete_64(table_t *t, uint64_t key); - -extern void *_nc_table_traverse_start(table_t *tin); -extern void *_nc_table_traverse(table_t *tin, void *ttin); -extern void _nc_table_traverse_end(table_t *tin, void *ttin); - -extern void _nc_table_free(table_t *tin); - -extern list_t *_nc_list_new(void *d); - -extern list_t *_nc_list_retain(list_t *l); -extern list_t *_nc_list_retain_list(list_t *l); - -extern void _nc_list_release(list_t *l); -extern void _nc_list_release_list(list_t *l); - -extern list_t *_nc_list_prev(list_t *l); -extern list_t *_nc_list_next(list_t *l); - -extern void _nc_list_set_next(list_t *l, list_t *n); -extern void _nc_list_set_prev(list_t *l, list_t *p); - -extern list_t *_nc_list_head(list_t *l); -extern list_t *_nc_list_tail(list_t *l); - -extern list_t *_nc_list_prepend(list_t *l, list_t *n); -extern list_t *_nc_list_append(list_t *l, list_t *n); - -extern list_t *_nc_list_concat(list_t *a, list_t *b); - -extern void *_nc_list_data(list_t *l); -extern void _nc_list_set_data(list_t *l, void *d); - -extern list_t *_nc_list_find(list_t *l, void *d); -extern list_t *_nc_list_find_release(list_t *l, void *d); - -extern list_t * _nc_list_reverse(list_t *l); -extern uint32_t _nc_list_count(list_t *l); -extern list_t *_nc_list_extract(list_t *n); -extern list_t *_nc_list_chop(list_t *l); - -#endif /* _NOTIFY_TABLE_H_ */ diff --git a/src/libnotify/notifyd/timer.c b/src/libnotify/notifyd/timer.c index 03ae0a331..c1ab31788 100644 --- a/src/libnotify/notifyd/timer.c +++ b/src/libnotify/notifyd/timer.c @@ -78,7 +78,7 @@ timer_next(timer_t *t, time_t now) /* shouldn't happen, as TIME_EVENT_CLOCK should always recur */ if (t->freq == 0) return 0; - x = ((t->freq - 1) + now - t->start) / t->freq; + x = (int32_t)(((t->freq - 1) + now - t->start) / t->freq); next = t->start + (x * t->freq); return next; } @@ -236,7 +236,6 @@ timer_next(timer_t *t, time_t now) } } - return 0; } /* @@ -247,7 +246,6 @@ static void timer_free(timer_t *t) { if (t == NULL) return; - if (t->deactivation_handler != NULL) Block_release(t->deactivation_handler); if (t->contextp != NULL) free(t->contextp); dispatch_release(t->t_src); @@ -300,25 +298,12 @@ timer_oneshot(time_t when, dispatch_queue_t queue) dispatch_source_set_event_handler(t->t_src, ^{ dispatch_source_merge_data(t->src, 1); dispatch_source_cancel(t->t_src); - if (t->deactivation_handler != NULL) - { - dispatch_async(t->t_queue, ^{ t->deactivation_handler(); }); - } }); - dispatch_resume(t->t_src); + dispatch_activate(t->t_src); return t; } -void -timer_set_deactivation_handler(timer_t *t, void(^handler)()) -{ - if (t == NULL) return; - - if (t->deactivation_handler != NULL) Block_release(t->deactivation_handler); - t->deactivation_handler = Block_copy(handler); -} - timer_t * timer_clock(time_t first, time_t freq_sec, time_t end, dispatch_queue_t queue) { @@ -347,7 +332,7 @@ timer_clock(time_t first, time_t freq_sec, time_t end, dispatch_queue_t queue) } t->end = end; - t->freq = freq_sec; + t->freq = (uint32_t)freq_sec; t->t_queue = queue; t->t_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); @@ -364,14 +349,10 @@ timer_clock(time_t first, time_t freq_sec, time_t end, dispatch_queue_t queue) if ((t->end > 0) && (t->end < (time(0) + freq_sec))) { dispatch_source_cancel(t->t_src); - if (t->deactivation_handler != NULL) - { - dispatch_async(t->t_queue, ^{ t->deactivation_handler(); }); - } } }); - dispatch_resume(t->t_src); + dispatch_activate(t->t_src); return t; } @@ -394,7 +375,7 @@ timer_calendar(time_t first, time_t freq_mth, time_t end, int day, dispatch_queu t->start = first; t->day = day; t->end = end; - t->freq = freq_mth; + t->freq = (uint32_t)freq_mth; t->t_queue = queue; t->t_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); @@ -415,10 +396,6 @@ timer_calendar(time_t first, time_t freq_mth, time_t end, int day, dispatch_queu if (x == 0) { dispatch_source_cancel(t->t_src); - if (t->deactivation_handler != NULL) - { - dispatch_async(t->t_queue, ^{ t->deactivation_handler(); }); - } } else { @@ -426,40 +403,7 @@ timer_calendar(time_t first, time_t freq_mth, time_t end, int day, dispatch_queu } }); - dispatch_resume(t->t_src); + dispatch_activate(t->t_src); return t; } - -timer_t * -timer_calendar_long(uint32_t start_year, uint32_t start_month, uint32_t start_day, uint32_t start_hour, uint32_t start_min, uint32_t start_sec, time_t freq, int day, uint32_t end_year, uint32_t end_month, uint32_t end_day, uint32_t end_hour, uint32_t end_min, uint32_t end_sec, dispatch_queue_t queue) -{ - struct tm tmp; - time_t first, last; - - memset(&tmp, 0, sizeof(struct tm)); - tmp.tm_year = start_year - 1900; - tmp.tm_mon = start_month; - tmp.tm_mday = start_day; - tmp.tm_isdst = -1; - tmp.tm_hour = start_hour; - tmp.tm_min = start_min; - tmp.tm_sec = start_sec; - - first = mktime(&tmp); - - if (freq == 0) return timer_oneshot(first, queue); - - memset(&tmp, 0, sizeof(struct tm)); - tmp.tm_year = end_year; - tmp.tm_mon = end_month; - tmp.tm_mday = end_day; - tmp.tm_isdst = -1; - tmp.tm_hour = end_hour; - tmp.tm_min = end_min; - tmp.tm_sec = end_sec; - - last = mktime(&tmp); - - return timer_calendar(first, freq, day, last, queue); -} diff --git a/src/libnotify/notifyd/timer.h b/src/libnotify/notifyd/timer.h index 9979656d2..84315ef18 100644 --- a/src/libnotify/notifyd/timer.h +++ b/src/libnotify/notifyd/timer.h @@ -35,25 +35,22 @@ */ typedef struct { - uint32_t type; - int64_t start; - int64_t end; - uint32_t freq; - int32_t day; - int64_t next; - void (^deactivation_handler)(); dispatch_source_t src; dispatch_source_t t_src; dispatch_queue_t t_queue; void *contextp; - uint32_t context32; + int64_t start; + int64_t end; + int64_t next; uint64_t context64; + uint32_t freq; + uint32_t type; + int32_t day; + uint32_t context32; } timer_t; timer_t *timer_oneshot(time_t when, dispatch_queue_t queue); timer_t *timer_clock(time_t first, time_t freq_sec, time_t end, dispatch_queue_t queue); timer_t *timer_calendar(time_t first, time_t freq_mth, time_t end, int day, dispatch_queue_t queue); -timer_t *timer_calendar_long(uint32_t yf, uint32_t mf, uint32_t df, uint32_t hf, uint32_t nf, uint32_t sf, time_t fm, int d, uint32_t ye, uint32_t me, uint32_t de, uint32_t he, uint32_t ne, uint32_t se, dispatch_queue_t queue); -void timer_set_deactivation_handler(timer_t *t, void(^handler)()); void timer_close(timer_t *t); diff --git a/src/libnotify/notifyd/xcodescripts/mk_notify_conf.sh b/src/libnotify/notifyd/xcodescripts/mk_notify_conf.sh old mode 100755 new mode 100644 index f03a3a0ac..5220033d0 --- a/src/libnotify/notifyd/xcodescripts/mk_notify_conf.sh +++ b/src/libnotify/notifyd/xcodescripts/mk_notify_conf.sh @@ -1,5 +1,5 @@ set -e -x -ETCDIR="$DSTROOT$INSTALL_PATH_PREFIX"/private/etc +ETCDIR="$DSTROOT"/private/etc install -d -o root -g wheel -m 0755 "$ETCDIR" install -c -o root -g wheel -m 0644 \ "$SRCROOT"/notifyd/"$NOTIFY_CONFIG" \ diff --git a/src/libnotify/notifyutil/notifyutil.c b/src/libnotify/notifyutil/notifyutil.c index 4aabd85b8..b2d493f44 100644 --- a/src/libnotify/notifyutil/notifyutil.c +++ b/src/libnotify/notifyutil/notifyutil.c @@ -33,6 +33,7 @@ #include #include #include +#include #define forever for(;;) #define IndexNull ((uint32_t)-1) @@ -42,6 +43,7 @@ #define PRINT_STATE 0x00000002 #define PRINT_TIME 0x00000004 #define PRINT_TYPE 0x00000008 +#define PRINT_TOKEN 0x00000010 #define PRINT_VERBOSE 0xffffffff #ifndef USEC_PER_SEC @@ -67,8 +69,6 @@ static const char *typename[] = "plain" }; -extern uint32_t notify_register_plain(const char *name, int *out_token); - typedef struct { uint32_t token; @@ -86,11 +86,11 @@ static int port_flag; static int file_flag; static int watch_file; static mach_port_t watch_port; -dispatch_source_t timer_src; -dispatch_source_t port_src; -dispatch_source_t file_src; -dispatch_source_t sig_src[__DARWIN_NSIG]; -dispatch_queue_t watch_queue; +static dispatch_source_t timer_src; +static dispatch_source_t port_src; +static dispatch_source_t file_src; +static dispatch_source_t sig_src[__DARWIN_NSIG]; +static dispatch_queue_t watch_queue; static void usage(const char *name) @@ -108,29 +108,33 @@ usage(const char *name) fprintf(stderr, " -signal [#] switch to signal [#] for subsequent registrations\n"); fprintf(stderr, " initial default for signal is 1 (SIGHUP)\n"); fprintf(stderr, " -dispatch switch to dispatch for subsequent registrations\n"); - fprintf(stderr, " -p key post a notifcation for key\n"); + fprintf(stderr, " -p key post a notification for key\n"); fprintf(stderr, " -w key register for key and report notifications\n"); fprintf(stderr, " -# key (# is an integer value, eg \"-1\") register for key and report # notifications\n"); fprintf(stderr, " -g key get state value for key\n"); fprintf(stderr, " -s key val set state value for key\n"); + + if(os_variant_has_internal_diagnostics(NULL)) + { + fprintf(stderr, " --dump dumps metadata to a file in /var/run/\n"); + } } -static const char * -notify_status_strerror(int status) +// Triggers a notifyd dump +static void +notifyutil_dump() { - switch (status) + int ret; + + ret = notify_dump_status("/var/run/notifyd.status"); + + if(ret == NOTIFY_STATUS_OK) { - case NOTIFY_STATUS_OK: return("OK"); - case NOTIFY_STATUS_INVALID_NAME: return "Invalid Name"; - case NOTIFY_STATUS_INVALID_TOKEN: return "Invalid Token"; - case NOTIFY_STATUS_INVALID_PORT: return "Invalid Port"; - case NOTIFY_STATUS_INVALID_FILE: return "Invalid File"; - case NOTIFY_STATUS_INVALID_SIGNAL: return "Invalid Signal"; - case NOTIFY_STATUS_INVALID_REQUEST: return "Invalid Request"; - case NOTIFY_STATUS_NOT_AUTHORIZED: return "Not Authorized"; - case NOTIFY_STATUS_FAILED: - default: return "Failed"; + fprintf(stdout, "Notifyd dump success! New file created at /var/run/notifyd.status\n"); + } else { + fprintf(stdout, "Notifyd dump failed with %x\n", ret); } + } static void @@ -191,15 +195,6 @@ reg_delete(uint32_t index) } } -static uint32_t -reg_find_name(const char *name) -{ - uint32_t i; - - for (i = 0; i < reg_count; i++) if (!strcmp(reg[i].name, name)) return i; - return IndexNull; -} - static uint32_t reg_find_token(uint32_t tid) { @@ -224,8 +219,15 @@ process_event(int tid) needspace = 0; + if (printopt & PRINT_TOKEN) + { + printf("[%d]", tid); + needspace = 1; + } + if (printopt & PRINT_TIME) { + if (needspace) printf(" "); snprintf(tstr, sizeof(tstr), "%llu", now.tv_usec + USEC_PER_SEC + 500); tstr[4] = '\0'; printf("%d.%s", (int)now.tv_sec, tstr+1); @@ -245,7 +247,7 @@ process_event(int tid) state = 0; status = notify_get_state(tid, &state); if (status == NOTIFY_STATUS_OK) printf("%llu",(unsigned long long)state); - else printf(": %s", notify_status_strerror(status)); + else printf(": Failed with code %d", status); needspace = 1; } @@ -264,6 +266,9 @@ process_event(int tid) status = notify_cancel(tid); reg_delete(index); } + fflush(stdout); + + if (reg_count == 0) exit(0); } static void @@ -323,9 +328,9 @@ signal_handler(uint32_t sig) } static void -dispatch_handler(const char *name) +dispatch_handler(int x) { - uint32_t index = reg_find_name(name); + uint32_t index = reg_find_token(x); if (index == IndexNull) return; process_event(reg[index].token); @@ -417,7 +422,7 @@ do_register(const char *name, uint32_t type, uint32_t signum, uint32_t count) case TYPE_DISPATCH: { - status = notify_register_dispatch(name, &tid, watch_queue, ^(int x){ dispatch_handler(name); }); + status = notify_register_dispatch(name, &tid, watch_queue, ^(int x){ dispatch_handler(x); }); if (status != NOTIFY_STATUS_OK) return status; break; } @@ -473,7 +478,7 @@ int main(int argc, const char *argv[]) { const char *name; - uint32_t i, n, index, signum, ntype, status, opts, nap; + uint32_t i, n, signum, ntype, status, opts, nap; int tid; uint64_t state; @@ -510,7 +515,7 @@ main(int argc, const char *argv[]) } else if (!strcmp(argv[i], "-M")) { - opts |= NOTIFY_OPT_DEMUX; + opts |= NOTIFY_OPT_DISPATCH; } else if (!strcmp(argv[i], "-R")) { @@ -620,6 +625,12 @@ main(int argc, const char *argv[]) fprintf(stderr, "value following -s name must be a 64-bit integer\n"); } } + else if (!strcmp(argv[i], "--dump") && os_variant_has_internal_diagnostics(NULL)) + { + notifyutil_dump(); + exit(0); + + } else { fprintf(stderr, "unrecognized option: %s\n", argv[i]); @@ -673,7 +684,7 @@ main(int argc, const char *argv[]) i++; status = notify_post(argv[i]); - if (status != NOTIFY_STATUS_OK) printf("%s: %s\n", argv[i], notify_status_strerror(status)); + if (status != NOTIFY_STATUS_OK) printf("%s: Failed with code %d\n", argv[i], status); else if (nap > 0) usleep(nap); } else if ((argv[i][0] == '-') && ((argv[i][1] == 'w') || ((argv[i][1] >= '0') && (argv[i][1] <= '9')))) @@ -690,15 +701,8 @@ main(int argc, const char *argv[]) i++; tid = IndexNull; - index = reg_find_name(argv[i]); - if (index != IndexNull) - { - fprintf(stderr, "Already watching for %s\n", argv[i]); - continue; - } - status = do_register(argv[i], ntype, signum, n); - if (status != NOTIFY_STATUS_OK) printf("%s: %s\n", argv[i], notify_status_strerror(status)); + if (status != NOTIFY_STATUS_OK) printf("%s: Failed with code %d\n", argv[i], status); } else if (!strcmp(argv[i], "-g")) { @@ -720,7 +724,7 @@ main(int argc, const char *argv[]) } if (status == NOTIFY_STATUS_OK) printf("%s %llu\n", argv[i], (unsigned long long)state); - else printf("%s: %s\n", argv[i], notify_status_strerror(status)); + else printf("%s: Failed with code %d\n", argv[i], status); } else if (!strcmp(argv[i], "-s")) { @@ -740,7 +744,7 @@ main(int argc, const char *argv[]) notify_cancel(tid); } - if (status != NOTIFY_STATUS_OK) printf("%s: %s\n", argv[i], notify_status_strerror(status)); + if (status != NOTIFY_STATUS_OK) printf("%s: Failed with code %d\n", argv[i], status); i++; } } diff --git a/src/libnotify/notifyutil/notifyutil_entitlements.plist b/src/libnotify/notifyutil/notifyutil_entitlements.plist new file mode 100644 index 000000000..5a7576c8d --- /dev/null +++ b/src/libnotify/notifyutil/notifyutil_entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.private.libnotify.statecapture + + + diff --git a/src/libnotify/os b/src/libnotify/os deleted file mode 120000 index 4c7dced22..000000000 --- a/src/libnotify/os +++ /dev/null @@ -1 +0,0 @@ -../libc/gen/ \ No newline at end of file diff --git a/src/libnotify/table.c b/src/libnotify/table.c index c6ef5e5a0..6a5144f0c 100644 --- a/src/libnotify/table.c +++ b/src/libnotify/table.c @@ -1,4 +1,3 @@ -// Modified by Lubos Dolezel for Darling build /* * Copyright (c) 2003-2011 Apple Inc. All rights reserved. * @@ -22,747 +21,104 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include #include #include #include #include +#include #include + #include "table.h" +#include "notify_internal.h" -#ifndef os_assumes -# define os_assumes(x) (x) -#endif +#define TABLE_TOMBSTONE ((void *)~0) +#define TABLE_MINSHIFT 5 +#define TABLE_MINSIZE (1 << TABLE_MINSHIFT) -#define KEY_UNKNOWN 0 -#define KEY_INT 1 -#define KEY_STR_MINE 2 -#define KEY_STR_SHARED 3 - -#define DEFAULT_SIZE 256 - -typedef struct table_node_s +OS_ALWAYS_INLINE +static inline uint32_t +table_next(uint32_t i, uint32_t size) { - union - { - char *string; - const char *const_string; - uint64_t uint64; - } key; - void *datum; - struct table_node_s *next; -} table_node_t; + i++; + return i >= size ? 0 : i; +} -typedef struct __table_private +OS_ALWAYS_INLINE +static inline uint32_t +table_prev(uint32_t i, uint32_t size) { - uint32_t type; - uint32_t bucket_count; - table_node_t **bucket; -} table_private_t; + return (i ? i : size) - 1; +} -typedef struct +static inline bool +string_equals(const char *a, const char *b) { - uint32_t bucket_index; - table_node_t *node; -} table_traverse_t; - -typedef struct __list_private -{ - struct __list_private *prev; - struct __list_private *next; - uint32_t refcount; - void *data; -} list_private_t; - -table_t * -_nc_table_new(uint32_t n) -{ - table_private_t *t; - - t = (table_t *)malloc(sizeof(table_t)); - if (t == NULL) return NULL; - - if (n == 0) n = DEFAULT_SIZE; - - t->type = KEY_UNKNOWN; - t->bucket_count = n; - t->bucket = (table_node_t **)calloc(t->bucket_count, sizeof(table_node_t *)); - if (t->bucket == NULL) - { - free(t); - return NULL; - } - - return (table_t *)t; + return a == b || strcmp(a, b) == 0; } static uint32_t -hash_key(int size, const char *key) +string_hash(const char *key) { - uint32_t v; - char *p; + uint32_t hash = 0; - if (key == NULL) return 0; + for (; *key; key++) { + hash += (unsigned char)(*key); + hash += (hash << 10); + hash ^= (hash >> 6); + } - v = 0; - for (p = (char *)key; *p != '\0'; p++) - { - v = (v << 1) ^ (v ^ *p); - } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); - v %= size; - return v; + return hash; } -static uint32_t -hash_nkey(uint32_t size, uint64_t key) +static inline bool +uint32_equals(uint32_t a, uint32_t b) { - uint32_t x = key; - uint32_t y = key >> 32; - return ((x ^ y) % size); + return a == b; } -void * -_nc_table_find_get_key(table_t *tin, const char *key, const char **shared_key) +static inline uint32_t +uint32_hash(uint32_t x) { - table_private_t *t; - table_node_t *n; - uint32_t b; - - if (tin == NULL) return NULL; - if (key == NULL) return NULL; - - if (shared_key != NULL) *shared_key = NULL; - - t = (table_private_t *)tin; - b = hash_key(t->bucket_count, key); - - for (n = t->bucket[b]; n != NULL; n = n->next) - { - if ((n->key.string != NULL) && (!strcmp(key, n->key.string))) - { - if (shared_key != NULL) *shared_key = n->key.const_string; - return n->datum; - } - } - - return NULL; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = (x >> 16) ^ x; + return x; } -void * -_nc_table_find(table_t *tin, const char *key) +static inline bool +uint64_equals(uint64_t a, uint64_t b) { - return _nc_table_find_get_key(tin, key, NULL); + return a == b; } -void * -_nc_table_find_64(table_t *tin, uint64_t key) +static inline uint32_t +uint64_hash(uint64_t key) { - table_private_t *t; - table_node_t *n; - uint32_t b; - - if (tin == NULL) return NULL; - - t = (table_private_t *)tin; - b = hash_nkey(t->bucket_count, key); - - for (n = t->bucket[b]; n != NULL; n = n->next) - { - if ((n->key.uint64 != (uint64_t)-1) && (key == n->key.uint64)) return n->datum; - } - - return NULL; + return uint32_hash((uint32_t)key ^ (uint32_t)(key >> 32)); } -void * -_nc_table_find_n(table_t *tin, uint32_t key) -{ - uint64_t n64 = key; - return _nc_table_find_64(tin, n64); -} - -static void -_nc_table_insert_type(table_t *tin, int type, char *key, const char *ckey, void *datum) -{ - table_private_t *t; - table_node_t *n; - uint32_t b; - - if (tin == NULL) return; - if ((key == NULL) && (ckey == NULL)) return; - if (datum == NULL) return; - - t = (table_private_t *)tin; - if (t->type == KEY_UNKNOWN) t->type = type; - else os_assumes(t->type == type); - - n = (table_node_t *)malloc(sizeof(table_node_t)); - - if (key != NULL) - { - b = hash_key(t->bucket_count, key); - n->key.string = key; - } - else - { - b = hash_key(t->bucket_count, ckey); - n->key.const_string = ckey; - } - - n->datum = datum; - n->next = t->bucket[b]; - t->bucket[b] = n; -} - -void -_nc_table_insert(table_t *tin, const char *key, void *datum) -{ - char *dup; - - if (tin == NULL) return; - if (key == NULL) return; - if (datum == NULL) return; - - dup = strdup(key); - if (dup == NULL) return; - - _nc_table_insert_type(tin, KEY_STR_MINE, dup, NULL, datum); -} - -void -_nc_table_insert_no_copy(table_t *tin, const char *key, void *datum) -{ - if (tin == NULL) return; - if (key == NULL) return; - if (datum == NULL) return; - - _nc_table_insert_type(tin, KEY_STR_SHARED, NULL, key, datum); -} - -void -_nc_table_insert_pass(table_t *tin, char *key, void *datum) -{ - if (tin == NULL) return; - if (key == NULL) return; - if (datum == NULL) return; - - _nc_table_insert_type(tin, KEY_STR_MINE, key, NULL, datum); -} - -void -_nc_table_insert_64(table_t *tin, uint64_t key, void *datum) -{ - table_private_t *t; - table_node_t *n; - uint32_t b; - - if (tin == NULL) return; - if (datum == NULL) return; - - t = (table_private_t *)tin; - if (t->type == KEY_UNKNOWN) t->type = KEY_INT; - else os_assumes(t->type == KEY_INT); - - b = hash_nkey(t->bucket_count, key); - n = (table_node_t *)malloc(sizeof(table_node_t)); - n->key.uint64 = key; - n->datum = datum; - n->next = t->bucket[b]; - t->bucket[b] = n; -} - -void -_nc_table_insert_n(table_t *tin, uint32_t key, void *datum) -{ - uint64_t n64 = key; - _nc_table_insert_64(tin, n64, datum); -} - -void -_nc_table_delete(table_t *tin, const char *key) -{ - table_private_t *t; - table_node_t *n, *p; - uint32_t b; - - if (tin == NULL) return; - if (key == NULL) return; - - t = (table_private_t *)tin; - os_assumes((t->type == KEY_STR_MINE) || (t->type == KEY_STR_SHARED)); - - b = hash_key(t->bucket_count, key); - - p = NULL; - for (n = t->bucket[b]; n != NULL; n = n->next) - { - if ((n->key.string != NULL) && (!strcmp(key, n->key.string))) - { - if (p == NULL) t->bucket[b] = n->next; - else p->next = n->next; - if (t->type == KEY_STR_MINE) free(n->key.string); - free(n); - return; - } - p = n; - } -} - -void -_nc_table_delete_64(table_t *tin, uint64_t key) -{ - table_private_t *t; - table_node_t *n, *p; - uint32_t b; - - if (tin == NULL) return; - - t = (table_private_t *)tin; - os_assumes(t->type == KEY_INT); - - b = hash_nkey(t->bucket_count, key); - - p = NULL; - for (n = t->bucket[b]; n != NULL; n = n->next) - { - if ((n->key.uint64 != (uint64_t)-1) && (key == n->key.uint64)) - { - if (p == NULL) t->bucket[b] = n->next; - else p->next = n->next; - free(n); - return; - } - p = n; - } -} - -void -_nc_table_delete_n(table_t *tin, uint32_t key) -{ - uint64_t n64 = key; - _nc_table_delete_64(tin, n64); -} - -void * -_nc_table_traverse_start(table_t *tin) -{ - table_traverse_t *tt; - table_private_t *t; - uint32_t b; - - if (tin == NULL) return NULL; - - t = (table_private_t *)tin; - if (t->bucket_count == 0) return NULL; - - for (b = 0; b < t->bucket_count; b++) - { - if (t->bucket[b] != NULL) - { - tt = (table_traverse_t *)malloc(sizeof(table_traverse_t)); - if (tt == NULL) return NULL; - tt->bucket_index = b; - tt->node = t->bucket[b]; - return (void *)tt; - } - } - - return NULL; -} - -void * -_nc_table_traverse(table_t *tin, void *ttin) -{ - table_private_t *t; - table_traverse_t *tt; - void *datum; - uint32_t b; - - if (tin == NULL) return NULL; - if (ttin == NULL) return NULL; - - t = (table_private_t *)tin; - tt = (table_traverse_t *)ttin; - - if (tt->node == NULL) return NULL; - - datum = tt->node->datum; - tt->node = tt->node->next; - if (tt->node != NULL) return datum; - - for (b = tt->bucket_index + 1; b < t->bucket_count; b++) - { - if (t->bucket[b] != NULL) - { - tt->bucket_index = b; - tt->node = t->bucket[b]; - return datum; - } - } - - tt->bucket_index = b; - tt->node = NULL; - - return datum; -} - -void -_nc_table_traverse_end(table_t *tin, void *ttin) -{ - if (ttin == NULL) return; - free(ttin); -} - -void -_nc_table_free(table_t *tin) -{ - table_private_t *t; - table_node_t *n, *x; - uint32_t b; - - if (tin == NULL) return; - - t = (table_private_t *)tin; - - for (b = 0; b < t->bucket_count; b++) - { - x = NULL; - for (n = t->bucket[b]; n != NULL; n = x) - { - x = n->next; - if (t->type == KEY_STR_MINE) free(n->key.string); - free(n); - } - } - - free(t->bucket); - free(t); -} - -/* Linked List */ - -/* - * Make a new node - */ -list_t * -_nc_list_new(void *d) -{ - list_t *n; - - n = (list_t *)calloc(1, sizeof(list_t)); - if (n == NULL) return NULL; - - n->refcount = 1; - n->data = d; - return n; -} - -/* - * Release a node - */ -void -_nc_list_release(list_t *l) -{ - if (l == NULL) return; - l->refcount--; - if (l->refcount > 0) return; - - free(l); -} - -/* - * Retain a node - */ -list_t * -_nc_list_retain(list_t *l) -{ - if (l == NULL) return NULL; - l->refcount++; - return l; -} - -/* - * Retain a list - */ -list_t * -_nc_list_retain_list(list_t *l) -{ - list_t *n; - - for (n = l; n != NULL; n = n->next) n->refcount++; - return l; -} - -/* - * Get previous node - */ -list_t * -_nc_list_prev(list_t *l) -{ - if (l == NULL) return NULL; - return l->prev; -} - -/* - * Get next node - */ -list_t * -_nc_list_next(list_t *l) -{ - if (l == NULL) return NULL; - return l->next; -} - -/* - * Get head (first node) of list - */ -list_t * -_nc_list_head(list_t *l) -{ - list_t *p; - - if (l == NULL) return NULL; - - for (p = l; p->prev != NULL; p = p->prev); - - return p; -} - -/* - * Get tail (last node) of list - */ -list_t * -_nc_list_tail(list_t *l) -{ - list_t *p; - - if (l == NULL) return NULL; - - for (p = l; p->next != NULL; p = p->next); - - return p; -} - -/* - * Insert a node in front of another node. - * Cuts list if n is NULL. - */ -list_t * -_nc_list_prepend(list_t *l, list_t *n) -{ - if (l == NULL) return n; - - if (n != NULL) - { - n->next = l; - n->prev = l->prev; - } - - if (l->prev != NULL) l->prev->next = n; - l->prev = n; - - return n; -} - -/* - * Append a node after another node. - * Cuts list if n is NULL. - */ -list_t * -_nc_list_append(list_t *l, list_t *n) -{ - if (l == NULL) return n; - - if (n != NULL) - { - n->prev = l; - n->next = l->next; - } - - if (l->next != NULL) n->next->prev = n; - l->next = n; - - return n; -} - -/* - * Set next pointer - use with care. - */ -void -_nc_list_set_next(list_t *l, list_t *n) -{ - if (l == NULL) return; - l->next = n; -} - -/* - * Set prev pointer - use with care. - */ -void -_nc_list_set_prev(list_t *l, list_t *p) -{ - if (l == NULL) return; - l->prev = p; -} - -/* - * Concatenate two lists. - * Returns new head. - */ -list_t * -_nc_list_concat(list_t *a, list_t *b) -{ - list_t *p; - - if (a == NULL) return b; - if (b == NULL) return a; - - for (p = a; p->next != NULL; p = p->next); - - p->next = b; - b->prev = p; - - for (p = a; p->prev != NULL; p = p->prev); - - return p; -} - -uint32_t -_nc_list_count(list_t *l) -{ - uint32_t n; - list_t *p; - - n = 0; - for (p = l; p != NULL; p = p->next) n++; - return n; -} - -void * -_nc_list_data(list_t *l) -{ - if (l == NULL) return NULL; - return l->data; -} - -void -_nc_list_set_data(list_t *l, void *d) -{ - if (l != NULL) l->data = d; -} - -list_t * -_nc_list_find(list_t *l, void *d) -{ - list_t *p; - - if (l == NULL) return NULL; - - for (p = l; p != NULL; p = p->next) - { - if (p->data == d) return p; - } - - return NULL; -} - -list_t * -_nc_list_find_release(list_t *l, void *d) -{ - list_t *p; - - if (l == NULL) return NULL; - - if (l->data == d) - { - p = l->next; - if (p != NULL) p->prev = NULL; - _nc_list_release(l); - return p; - } - - for (p = l->next; p != NULL; p = p->next) - { - if (p->data == d) - { - p->prev->next = p->next; - if (p->next != NULL) p->next->prev = p->prev; - _nc_list_release(p); - return l; - } - } - - return l; -} - -list_t * -_nc_list_reverse(list_t *l) -{ - list_t *x, *s, *r; - - if (l == NULL) return NULL; - - x = l->prev; - r = l; - s = l->next; - - while (s != NULL) - { - s = r->next; - r->next = r->prev; - r->prev = s; - if (s != NULL) r = s; - } - - if (x != NULL) - { - x->next = r; - r->prev = x; - } - - return r; -} - -list_t * -_nc_list_extract(list_t *n) -{ - if (n == NULL) return NULL; - - if (n->prev != NULL) n->prev->next = n->next; - if (n->next != NULL) n->next->prev = n->prev; - - n->prev = NULL; - n->next = NULL; - - return n; -} - -list_t * -_nc_list_chop(list_t *l) -{ - list_t *p; - - if (l == NULL) return NULL; - p = l->next; - if (p != NULL) p->prev = NULL; - - _nc_list_release(l); - return p; -} - -void -_nc_list_release_list(list_t *l) -{ - list_t *p, *n; - - if (l == NULL) return; - if (l->prev != NULL) l->prev->next = NULL; - - p = l; - while (p != NULL) - { - n = p->next; - _nc_list_release(p); - p = n; - } -} +#define ns(n) _nc_table##n +#define key_t char * +#define ckey_t const char * +#define key_hash string_hash +#define key_equals string_equals +#include "table.in.c" + +#define ns(n) _nc_table##n##_n +#define key_t uint32_t +#define ckey_t uint32_t +#define key_hash uint32_hash +#define key_equals uint32_equals +#include "table.in.c" + +#define ns(n) _nc_table##n##_64 +#define key_t uint64_t +#define ckey_t uint64_t +#define key_hash uint64_hash +#define key_equals uint64_equals +#include "table.in.c" diff --git a/src/libnotify/table.h b/src/libnotify/table.h index 9ca80cd0e..53762ff14 100644 --- a/src/libnotify/table.h +++ b/src/libnotify/table.h @@ -24,65 +24,45 @@ #ifndef _NOTIFY_TABLE_H_ #define _NOTIFY_TABLE_H_ +#include #include -typedef struct __table_private table_t; -typedef struct __list_private list_t; +#define _nc_table(key_t, _ns) \ + struct _nc_table##_ns { \ + uint32_t count; \ + uint32_t tombstones; \ + uint32_t size; \ + uint16_t grow_shift; \ + uint16_t key_offset; \ + key_t **keys; \ + } -extern table_t *_nc_table_new(uint32_t n); +typedef _nc_table(char *, ) table_t; +typedef _nc_table(uint32_t, _n) table_n_t; +typedef _nc_table(uint64_t, _64) table_64_t; -extern void _nc_table_insert(table_t *t, const char *key, void *datum); -extern void _nc_table_insert_no_copy(table_t *t, const char *key, void *datum); -extern void _nc_table_insert_pass(table_t *t, char *key, void *datum); -extern void _nc_table_insert_n(table_t *t, uint32_t key, void *datum); -extern void _nc_table_insert_64(table_t *t, uint64_t key, void *datum); +__BEGIN_DECLS + +extern void _nc_table_init(table_t *t, size_t key_offset); +extern void _nc_table_init_n(table_n_t *t, size_t key_offset); +extern void _nc_table_init_64(table_64_t *t, size_t key_offset); + +extern void _nc_table_insert(table_t *t, char **key); +extern void _nc_table_insert_n(table_n_t *t, uint32_t *key); +extern void _nc_table_insert_64(table_64_t *t, uint64_t *key); extern void *_nc_table_find(table_t *t, const char *key); -extern void *_nc_table_find_get_key(table_t *tin, const char *key, const char **shared_key); -extern void *_nc_table_find_n(table_t *t, uint32_t key); -extern void *_nc_table_find_64(table_t *t, uint64_t key); +extern void *_nc_table_find_n(table_n_t *t, uint32_t key); +extern void *_nc_table_find_64(table_64_t *t, uint64_t key); extern void _nc_table_delete(table_t *t, const char *key); -extern void _nc_table_delete_n(table_t *t, uint32_t key); -extern void _nc_table_delete_64(table_t *t, uint64_t key); +extern void _nc_table_delete_n(table_n_t *t, uint32_t key); +extern void _nc_table_delete_64(table_64_t *t, uint64_t key); -extern void *_nc_table_traverse_start(table_t *tin); -extern void *_nc_table_traverse(table_t *tin, void *ttin); -extern void _nc_table_traverse_end(table_t *tin, void *ttin); +extern void _nc_table_foreach(table_t *t, OS_NOESCAPE bool (^)(void *)); +extern void _nc_table_foreach_n(table_n_t *t, OS_NOESCAPE bool (^)(void *)); +extern void _nc_table_foreach_64(table_64_t *t, OS_NOESCAPE bool (^)(void *)); -extern void _nc_table_free(table_t *tin); - -extern list_t *_nc_list_new(void *d); - -extern list_t *_nc_list_retain(list_t *l); -extern list_t *_nc_list_retain_list(list_t *l); - -extern void _nc_list_release(list_t *l); -extern void _nc_list_release_list(list_t *l); - -extern list_t *_nc_list_prev(list_t *l); -extern list_t *_nc_list_next(list_t *l); - -extern void _nc_list_set_next(list_t *l, list_t *n); -extern void _nc_list_set_prev(list_t *l, list_t *p); - -extern list_t *_nc_list_head(list_t *l); -extern list_t *_nc_list_tail(list_t *l); - -extern list_t *_nc_list_prepend(list_t *l, list_t *n); -extern list_t *_nc_list_append(list_t *l, list_t *n); - -extern list_t *_nc_list_concat(list_t *a, list_t *b); - -extern void *_nc_list_data(list_t *l); -extern void _nc_list_set_data(list_t *l, void *d); - -extern list_t *_nc_list_find(list_t *l, void *d); -extern list_t *_nc_list_find_release(list_t *l, void *d); - -extern list_t * _nc_list_reverse(list_t *l); -extern uint32_t _nc_list_count(list_t *l); -extern list_t *_nc_list_extract(list_t *n); -extern list_t *_nc_list_chop(list_t *l); +__END_DECLS #endif /* _NOTIFY_TABLE_H_ */ diff --git a/src/libnotify/table.in.c b/src/libnotify/table.in.c new file mode 100644 index 000000000..e798de9da --- /dev/null +++ b/src/libnotify/table.in.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2018 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +static inline void * +ns(_value)(struct ns() *t, uint32_t i) +{ + return (void *)((uintptr_t)t->keys[i] - t->key_offset); +} + +static inline void +ns(_clear)(struct ns() *t) +{ + free(t->keys); + ns(_init)(t, t->key_offset); +} + +void +ns(_init)(struct ns() *t, size_t offset) +{ + *t = (struct ns()){ + .grow_shift = TABLE_MINSHIFT, + .key_offset = (uint16_t)offset, + }; +} + +OS_NOINLINE +static void +ns(_rehash)(struct ns() *t, int direction) +{ + struct ns() old = *t; + + if (direction > 0) { + t->size += (1 << t->grow_shift); + if (t->size == (8 << t->grow_shift)) { + t->grow_shift++; + } + } else if (direction < 0) { + if (t->grow_shift > TABLE_MINSHIFT) { + t->grow_shift--; + } + t->size = roundup(t->size / 2, (1 << t->grow_shift)); + } + + t->count = 0; + t->tombstones = 0; + t->keys = calloc(t->size, sizeof(key_t *)); + if (t->keys == NULL) { + NOTIFY_INTERNAL_CRASH(0, "Unable to grow table: registration leak?"); + } + + for (uint32_t i = 0; i < old.size; i++) { + if (old.keys[i] == NULL || old.keys[i] == TABLE_TOMBSTONE) { + continue; + } + + ns(_insert)(t, old.keys[i]); + } + free(old.keys); +} + +void * +ns(_find)(struct ns() *t, ckey_t key) +{ + if (t->count == 0) { + return NULL; + } + + uint32_t size = t->size, loop_limit = t->size; + uint32_t i = key_hash(key) % size; + + for (;;) { + if (os_unlikely(loop_limit-- == 0)) { + NOTIFY_INTERNAL_CRASH(0, "Corrupt hash table"); + } + if (t->keys[i] != TABLE_TOMBSTONE) { + if (t->keys[i] == NULL) { + return NULL; + } + if (key_equals(key, *t->keys[i])) { + return ns(_value)(t, i); + } + } + i = table_next(i, size); + } +} + +void +ns(_insert)(struct ns() *t, key_t *key) +{ + /* + * Our algorithm relies on having enough NULLS to end loops. + * Make sure their density is never below 25%. + * + * When it drops too low, if the ratio of tombstones is low, + * assume we're on a growth codepath. + * + * Else, we just rehash in place to prune tombstones. + */ + if (os_unlikely(t->count + t->tombstones >= 3 * t->size / 4)) { + if (t->count >= 4 * t->tombstones) { + ns(_rehash)(t, 1); + } else { + ns(_rehash)(t, 0); + } + } + + uint32_t size = t->size, loop_limit = t->size; + uint32_t i = key_hash(*key) % size; + + for (;;) { + if (os_unlikely(loop_limit-- == 0)) { + NOTIFY_INTERNAL_CRASH(0, "Corrupt hash table"); + } + if (t->keys[i] == NULL) { + break; + } + if (t->keys[i] == TABLE_TOMBSTONE) { + t->tombstones--; + break; + } + i = table_next(i, size); + } + + t->keys[i] = key; + t->count++; +} + +void +ns(_delete)(struct ns() *t, ckey_t key) +{ + if (t->count == 0) { + return; + } + + uint32_t size = t->size, loop_limit = t->size; + uint32_t i = key_hash(key) % size; + + for (;;) { + if (os_unlikely(loop_limit-- == 0)) { + NOTIFY_INTERNAL_CRASH(0, "Corrupt hash table"); + } + if (t->keys[i] != TABLE_TOMBSTONE) { + if (t->keys[i] == NULL) { + return; + } + if (key_equals(key, *t->keys[i])) { + break; + } + } + i = table_next(i, size); + } + + t->keys[i] = TABLE_TOMBSTONE; + t->tombstones++; + t->count--; + + if (t->keys[table_next(i, size)] == NULL) { + do { + t->tombstones--; + t->keys[i] = NULL; + i = table_prev(i, size); + } while (t->keys[i] == TABLE_TOMBSTONE); + } + + if (t->count == 0) { + /* if the table is empty, free all its resources */ + ns(_clear)(t); + } else if (t->size >= TABLE_MINSIZE * 2 && t->count < t->size / 8) { + /* if the table density drops below 12%, shrink it */ + ns(_rehash)(t, -1); + } +} + +void +ns(_foreach)(struct ns() *t, bool (^handler)(void *)) +{ + for (uint32_t i = 0; i < t->size; i++) { + if (t->keys[i] != NULL && t->keys[i] != TABLE_TOMBSTONE) { + if (!handler(ns(_value)(t, i))) break; + } + } +} + +#undef ns +#undef key_t +#undef ckey_t +#undef key_hash +#undef key_equals +#undef make_map diff --git a/src/libnotify/xcodeconfig/base.xcconfig b/src/libnotify/xcodeconfig/base.xcconfig index e09d92619..ea7c95b69 100644 --- a/src/libnotify/xcodeconfig/base.xcconfig +++ b/src/libnotify/xcodeconfig/base.xcconfig @@ -1,7 +1,7 @@ #include "/Makefiles/CoreOS/Xcode/BSD.xcconfig" -#include "/AppleInternal/XcodeConfig/SimulatorSupport.xcconfig" -INSTALL_PATH[sdk=macosx*] = $(INSTALL_PATH_ACTUAL) +// Architectures +SDKROOT = macosx.internal ALWAYS_SEARCH_USER_PATHS = YES HEADER_SEARCH_PATHS = $(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders $(PROJECT_DIR) @@ -12,18 +12,22 @@ GCC_ENABLE_CPP_EXCEPTIONS = NO GCC_ENABLE_CPP_RTTI = NO GCC_ENABLE_OBJC_EXCEPTIONS = NO GCC_PREPROCESSOR_DEFINITIONS = __DARWIN_NON_CANCELABLE=1 -GCC_C_LANGUAGE_STANDARD = gnu99 +GCC_C_LANGUAGE_STANDARD = gnu11 GCC_WARN_ABOUT_RETURN_TYPE = YES GCC_WARN_UNUSED_VARIABLE = YES //GCC_WARN_64_TO_32_BIT_CONVERSION = YES //GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES //GCC_WARN_ABOUT_RETURN_TYPE = YES -OTHER_CFLAGS = -fno-exceptions +OTHER_CFLAGS = -fno-exceptions -Weverything -Wno-reserved-id-macro -Wno-padded -Wno-sign-compare -Wno-used-but-marked-unused -Wno-undef -Wno-cast-qual -Wno-documentation -Wno-unused-macros -Wno-gnu OTHER_CFLAGS_debug = -O0 CURRENT_PROJECT_VERSION = CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion) VERSION_INFO_PREFIX = __ VERSIONING_SYSTEM = apple-generic PREBINDING = NO +LLVM_LTO = YES + NOTIFY_CONFIG = notify.conf.MacOSX NOTIFY_CONFIG[sdk=iphoneos*] = notify.conf.iPhone +NOTIFY_CONFIG[sdk=tvos*] = notify.conf.iPhone +NOTIFY_CONFIG[sdk=*simulator*] = notify.conf.iOSSimulator diff --git a/src/libnotify/xcodeconfig/libnotify.xcconfig b/src/libnotify/xcodeconfig/libnotify.xcconfig index 7a045d981..8d3bc6e12 100644 --- a/src/libnotify/xcodeconfig/libnotify.xcconfig +++ b/src/libnotify/xcodeconfig/libnotify.xcconfig @@ -1,19 +1,22 @@ #include "base.xcconfig" PRODUCT_NAME = libsystem_notify -INSTALL_PATH_ACTUAL = /usr/lib/system -PRIVATE_HEADERS_FOLDER_PATH = $(INSTALL_PATH_PREFIX)/usr/local/include -PUBLIC_HEADERS_FOLDER_PATH = $(INSTALL_PATH_PREFIX)/usr/include +INSTALL_PATH = /usr/lib/system +PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include +PUBLIC_HEADERS_FOLDER_PATH = /usr/include DYLIB_CURRENT_VERSION = $(CURRENT_PROJECT_VERSION) EXECUTABLE_PREFIX = BUILD_VARIANTS = normal CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion) VERSION_INFO_PREFIX = __ VERSIONING_SYSTEM = apple-generic +GCC_SYMBOLS_PRIVATE_EXTERN = YES +STRIP_STYLE = non-global +SUPPORTS_TEXT_BASED_API = YES +TAPI_VERIFY_MODE = Pedantic LINK_WITH_STANDARD_LIBRARIES = NO -OTHER_LDFLAGS = -umbrella System -L/usr/lib/system $(LDFLAGS_DYLD) $(LDFLAGS_COMPILER_RT) $(LDFLAGS_SYSCALL) $(LDFLAGS_PLATFORM) $(LDFLAGS_PTHREAD) $(LDFLAGS_MALLOC) $(LDFLAGS_C) $(LDFLAGS_BLOCKS) $(LDFLAGS_DISPATCH) $(LDFLAGS_XPC) - +OTHER_LDFLAGS = -umbrella System -L/usr/lib/system $(LDFLAGS_DYLD) $(LDFLAGS_COMPILER_RT) $(LDFLAGS_SYSCALL) $(LDFLAGS_PLATFORM) $(LDFLAGS_PTHREAD) $(LDFLAGS_MALLOC) $(LDFLAGS_C) $(LDFLAGS_BLOCKS) $(LDFLAGS_DISPATCH) $(LDFLAGS_XPC) $(LDFLAGS_DARWIN) LDFLAGS_DYLD = -ldyld LDFLAGS_COMPILER_RT = -lcompiler_rt LDFLAGS_SYSCALL = -lsystem_kernel @@ -24,8 +27,9 @@ LDFLAGS_PTHREAD = -lsystem_pthread LDFLAGS_PTHREAD[sdk=iphonesimulator*] = -lsystem_sim_pthread LDFLAGS_MALLOC = -lsystem_malloc LDFLAGS_C = -lsystem_c -LDFLAGS_C[sdk=iphonesimulator*] = -lsystem_sim_c LDFLAGS_BLOCKS = -lsystem_blocks -LDFLAGS_BLOCKS[sdk=iphonesimulator*] = -lsystem_sim_blocks LDFLAGS_DISPATCH = -ldispatch LDFLAGS_XPC = -lxpc +LDFLAGS_DARWIN = -lsystem_darwin + +IS_ZIPPERED=YES diff --git a/src/libnotify/xcodeconfig/notifyd.xcconfig b/src/libnotify/xcodeconfig/notifyd.xcconfig index 53faec8d5..16793c706 100644 --- a/src/libnotify/xcodeconfig/notifyd.xcconfig +++ b/src/libnotify/xcodeconfig/notifyd.xcconfig @@ -1,5 +1,11 @@ #include "base.xcconfig" -INSTALL_PATH_ACTUAL = /usr/sbin +INSTALL_PATH = /usr/sbin + +// Sandbox settings +SANDBOX_PATH = /System/Library/Sandbox/Profiles +SANDBOX_PATH[sdk=iphone*] = +SANDBOX_NAME = com.apple.notifyd +SANDBOX_PROFILE = com.apple.notifyd.sb +OTHER_MIGFLAGS = -DMACH_NOTIFY_SEND_POSSIBLE_EXPECTED=1 -OTHER_LDFLAGS = -lCrashReporterClient diff --git a/src/libnotify/xcodeconfig/notifyutil.xcconfig b/src/libnotify/xcodeconfig/notifyutil.xcconfig index b4d6d9338..0c00b7e7b 100644 --- a/src/libnotify/xcodeconfig/notifyutil.xcconfig +++ b/src/libnotify/xcodeconfig/notifyutil.xcconfig @@ -1,4 +1,4 @@ #include "base.xcconfig" -INSTALL_PATH_ACTUAL = /usr/bin +INSTALL_PATH = /usr/bin diff --git a/src/libnotify/xcodescripts/no-sim-man.sh b/src/libnotify/xcodescripts/no-sim-man.sh old mode 100755 new mode 100644 index 282dfe11c..773350da4 --- a/src/libnotify/xcodescripts/no-sim-man.sh +++ b/src/libnotify/xcodescripts/no-sim-man.sh @@ -1,5 +1,5 @@ #!/bin/bash -ex if [[ "${PLATFORM_NAME}" =~ "simulator" ]]; then - rm -rf ${DSTROOT}${INSTALL_PATH_PREFIX}/usr/share/man + rm -rf ${DSTROOT}/usr/share/man fi diff --git a/src/libnotify/xcodescripts/sim-compat-symlink.sh b/src/libnotify/xcodescripts/sim-compat-symlink.sh old mode 100755 new mode 100644 diff --git a/src/libnotify/xctests/Info.plist b/src/libnotify/xctests/Info.plist new file mode 100644 index 000000000..6c40a6cd0 --- /dev/null +++ b/src/libnotify/xctests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/src/libnotify/xctests/RegisterTests.m b/src/libnotify/xctests/RegisterTests.m new file mode 100644 index 000000000..2b3fb76d5 --- /dev/null +++ b/src/libnotify/xctests/RegisterTests.m @@ -0,0 +1,162 @@ +// +// xctests.m +// xctests +// + +#include +#include +#include +#include + +#include "notify_internal.h" + +#import + +@interface RegisterTests : XCTestCase + +@end + +@implementation RegisterTests + +static dispatch_queue_t noteQueue; + ++ (void)setUp +{ + noteQueue = dispatch_queue_create("noteQ", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); +} + ++ (void)tearDown +{ + noteQueue = nil; +} + +- (void)tearDown +{ + [super tearDown]; +} + +- (void)test00RegisterSimple +{ + static int token; + XCTAssert(notify_register_dispatch("com.example.test.simple", &token, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(token) == NOTIFY_STATUS_OK, @"notify_cancel failed"); +} + +- (void)test01RegisterNested +{ + for (int i = 0; i < 100000; i++) { + static int token1, token2; + XCTAssert(notify_register_dispatch("com.example.test.multiple", &token1, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_register_dispatch("com.example.test.multiple", &token2, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(token1) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + XCTAssert(notify_cancel(token2) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + } +} + +- (void)test02RegisterInterleaved +{ + for (int i = 0; i < 100000; i++) { + static int token1, token2; + XCTAssert(notify_register_dispatch("com.example.test.interleaved", &token1, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(token1) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + XCTAssert(notify_register_dispatch("com.example.test.interleaved", &token2, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(token2) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + } +} + +- (void)test03RegisterRaceWithDealloc +{ + static int tokens[1000000]; + dispatch_apply(1000000, DISPATCH_APPLY_AUTO, ^(size_t i) { + XCTAssert(notify_register_check("com.example.test.race", &tokens[i]) == NOTIFY_STATUS_OK, @"notify_register_check failed"); + XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + XCTAssert(notify_register_dispatch("com.example.test.race", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + XCTAssert(notify_post("com.example.test.race") == NOTIFY_STATUS_OK, @"notify_post failed"); + }); +} + +- (void)test04RegisterManyTokens +{ + static int tokens[100000]; + dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) { + XCTAssert(notify_register_dispatch("com.example.test.many", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + }); +} + +- (void)test05RegisterBulkCancel +{ + static int tokens[100000]; + dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) { + XCTAssert(notify_register_dispatch("com.example.test.bulk", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + }); + + dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) { + XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + }); +} + +- (void)test06RegisterSimpleSelf +{ + static int token; + XCTAssert(notify_register_dispatch("self.example.test.simple", &token, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(token) == NOTIFY_STATUS_OK, @"notify_cancel failed"); +} + +- (void)test07RegisterNestedSelf +{ + for (int i = 0; i < 100000; i++) { + static int token1, token2; + XCTAssert(notify_register_dispatch("self.example.test.multiple", &token1, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_register_dispatch("self.example.test.multiple", &token2, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(token1) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + XCTAssert(notify_cancel(token2) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + } +} + +- (void)test08RegisterInterleavedSelf +{ + for (int i = 0; i < 100000; i++) { + static int token1, token2; + XCTAssert(notify_register_dispatch("self.example.test.interleaved", &token1, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(token1) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + XCTAssert(notify_register_dispatch("self.example.test.interleaved", &token2, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(token2) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + } +} + +- (void)test09RegisterRaceWithDeallocSelf +{ + static int tokens[1000000]; + dispatch_apply(1000000, DISPATCH_APPLY_AUTO, ^(size_t i) { + XCTAssert(notify_register_check("self.example.test.race", &tokens[i]) == NOTIFY_STATUS_OK, @"notify_register_check failed"); + XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + XCTAssert(notify_register_dispatch("self.example.test.race", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + XCTAssert(notify_post("com.example.test.race") == NOTIFY_STATUS_OK, @"notify_post failed"); + }); +} + +- (void)test10RegisterManyTokensSelf +{ + static int tokens[100000]; + dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) { + XCTAssert(notify_register_dispatch("self.example.test.many", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + }); +} + +- (void)test11RegisterBulkCancelSelf +{ + static int tokens[100000]; + dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) { + XCTAssert(notify_register_dispatch("self.example.test.bulk", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed"); + }); + + dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) { + XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed"); + }); +} + +@end