Update Source To Libnotify-279.40.4

This commit is contained in:
Thomas A 2022-04-23 18:26:49 -07:00
parent 4fd1e8f5ff
commit c8f232db15
52 changed files with 3846 additions and 1745 deletions

View File

@ -34,6 +34,7 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
1864C88E23F227D200A383C1 /* notify_test_helper.c in Sources */ = {isa = PBXBuildFile; fileRef = 1864C88223F2174E00A383C1 /* notify_test_helper.c */; };
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 */; };
@ -60,7 +61,6 @@
3FA21AD0148AAA5000099D2F /* notifyd.c in Sources */ = {isa = PBXBuildFile; fileRef = 3FA21A9E148AA7FA00099D2F /* notifyd.c */; };
3FA21AD1148AAA5000099D2F /* pathwatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 3FA21AA0148AA7FA00099D2F /* pathwatch.c */; };
3FA21AD2148AAA5000099D2F /* service.c in Sources */ = {isa = PBXBuildFile; fileRef = 3FA21AA2148AA7FA00099D2F /* service.c */; };
3FA21AD3148AAA5000099D2F /* timer.c in Sources */ = {isa = PBXBuildFile; fileRef = 3FA21AA5148AA7FA00099D2F /* timer.c */; };
3FA21AD4148AAA5D00099D2F /* notifyd.8 in Install man page */ = {isa = PBXBuildFile; fileRef = 3FA21A9D148AA7FA00099D2F /* notifyd.8 */; };
3FA21AD5148AAA6E00099D2F /* notifyutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 3FA21AA9148AA82700099D2F /* notifyutil.c */; };
3FA21AD6148AAA7500099D2F /* notifyutil.1 in Install man page */ = {isa = PBXBuildFile; fileRef = 3FA21AA8148AA82700099D2F /* notifyutil.1 */; };
@ -81,6 +81,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
1864C88C23F227C200A383C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 1864C88323F227AC00A383C1;
remoteInfo = notfity_test_helper;
};
3FA21ACA148AA94A00099D2F /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
@ -185,14 +192,20 @@
/* Begin PBXFileReference section */
180F8D3E21136EBF009A472B /* notify_pathwatch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notify_pathwatch.c; sourceTree = "<group>"; };
18177A7123612EDC008CCFDE /* notify_register_mach_port.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notify_register_mach_port.c; sourceTree = "<group>"; };
182FD2C2210BB0F600BF263C /* notify_benchmark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_benchmark.c; sourceTree = "<group>"; };
18314CC821FF867600FEB43D /* notifyutil_entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = notifyutil_entitlements.plist; sourceTree = "<group>"; };
18383C6720F909A300D5C465 /* notify_regenerate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_regenerate.c; sourceTree = "<group>"; };
18383C6820F909A300D5C465 /* notify_register_signal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_register_signal.c; sourceTree = "<group>"; };
18383C6920F909A300D5C465 /* notify_sigusr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_sigusr.c; sourceTree = "<group>"; };
183F91E32357FCEB009A1BFC /* notify_suspend.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notify_suspend.c; sourceTree = "<group>"; };
1846D85A24AAC578001D052D /* notify_register_file_desc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notify_register_file_desc.c; sourceTree = "<group>"; };
1864C88123F216CB00A383C1 /* notify_exec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_exec.c; sourceTree = "<group>"; };
1864C88223F2174E00A383C1 /* notify_test_helper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notify_test_helper.c; sourceTree = "<group>"; };
1864C88B23F227AC00A383C1 /* notify_test_helper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = notify_test_helper; sourceTree = BUILT_PRODUCTS_DIR; };
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 = "<group>"; usesTabs = 1; };
2D312B74102CA2E300F90022 /* notify_client.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_client.c; sourceTree = "<group>"; usesTabs = 1; };
2D312B74102CA2E300F90022 /* notify_client.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_client.c; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; };
2D312B75102CA2E300F90022 /* table.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = table.c; sourceTree = "<group>"; };
2D312B79102CA30200F90022 /* notify_ipc.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = notify_ipc.defs; sourceTree = "<group>"; };
2D312B7C102CA32500F90022 /* notify_keys.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; fileEncoding = 4; path = notify_keys.h; sourceTree = "<group>"; };
@ -217,7 +230,6 @@
3F82235D12B18551005DD509 /* libnotify.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libnotify.xcconfig; sourceTree = "<group>"; };
3F8223B412B18877005DD509 /* libsystem_notify.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_notify.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
3F947780191C322100A93E8E /* no-sim-man.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "no-sim-man.sh"; sourceTree = "<group>"; };
3F947781191C322100A93E8E /* sim-compat-symlink.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "sim-compat-symlink.sh"; sourceTree = "<group>"; };
3F999961185C474E00EAD3A0 /* base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = "<group>"; };
3F999963185C474E00EAD3A0 /* notifyd.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = notifyd.xcconfig; sourceTree = "<group>"; };
3F999964185C474E00EAD3A0 /* notifyutil.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = notifyutil.xcconfig; sourceTree = "<group>"; };
@ -232,8 +244,6 @@
3FA21AA1148AA7FA00099D2F /* pathwatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pathwatch.h; sourceTree = "<group>"; };
3FA21AA2148AA7FA00099D2F /* service.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = service.c; sourceTree = "<group>"; usesTabs = 1; };
3FA21AA3148AA7FA00099D2F /* service.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = service.h; sourceTree = "<group>"; };
3FA21AA5148AA7FA00099D2F /* timer.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = timer.c; sourceTree = "<group>"; };
3FA21AA6148AA7FA00099D2F /* timer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = timer.h; sourceTree = "<group>"; };
3FA21AA8148AA82700099D2F /* notifyutil.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = notifyutil.1; sourceTree = "<group>"; };
3FA21AA9148AA82700099D2F /* notifyutil.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notifyutil.c; sourceTree = "<group>"; };
3FA21AB0148AA8E300099D2F /* notifyd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = notifyd; sourceTree = BUILT_PRODUCTS_DIR; };
@ -252,11 +262,14 @@
727C90391B9A372700D5B754 /* notify_disable_test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_disable_test.c; sourceTree = "<group>"; };
727C903A1B9A372700D5B754 /* notify_many_dups.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_many_dups.c; sourceTree = "<group>"; };
727C903B1B9A372700D5B754 /* random_test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = random_test.c; sourceTree = "<group>"; };
7875226923A34F3B002B6F1D /* entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = entitlements.plist; sourceTree = "<group>"; };
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 = "<group>"; };
94099C772087E7D50004B6BC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9487BF2820882DAD0043BF74 /* parallel_register_cancel.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = parallel_register_cancel.c; sourceTree = "<group>"; };
9B71D22F206AB52200BB9574 /* notify_qos.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_qos.c; sourceTree = "<group>"; };
C0FF3C23234516ED00ABBA89 /* notify_matching.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = notify_matching.c; sourceTree = "<group>"; };
C0FF3C242345172700ABBA89 /* notify_leaks.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = notify_leaks.c; sourceTree = "<group>"; };
E6481E7E2165785F00C04412 /* notify_probes.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; path = notify_probes.d; sourceTree = "<group>"; };
FC7B7A52155781930064D203 /* notify_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = notify_internal.h; sourceTree = "<group>"; usesTabs = 1; };
/* End PBXFileReference section */
@ -320,6 +333,7 @@
94099C732087E7D50004B6BC /* xctests.xctest */,
1886391720E1A22F00C8BEA9 /* notifybench */,
6EC91ED32178FF7100F11587 /* Frameworks */,
1864C88B23F227AC00A383C1 /* notify_test_helper */,
);
name = Libnotify;
sourceTree = "<group>";
@ -383,7 +397,6 @@
isa = PBXGroup;
children = (
3F947780191C322100A93E8E /* no-sim-man.sh */,
3F947781191C322100A93E8E /* sim-compat-symlink.sh */,
);
path = xcodescripts;
sourceTree = "<group>";
@ -415,12 +428,13 @@
3FA21A98148AA7FA00099D2F /* notifyd */ = {
isa = PBXGroup;
children = (
7875226923A34F3B002B6F1D /* entitlements.plist */,
2DD962061B14E6560040D341 /* com.apple.notifyd.sb */,
3FA21ADC148AABA900099D2F /* Build Support */,
3FA21A99148AA7FA00099D2F /* com.apple.notifyd.plist */,
3FA21A9A148AA7FA00099D2F /* notify.conf */,
3FA21AAA148AA85300099D2F /* Source */,
3FA21A9D148AA7FA00099D2F /* notifyd.8 */,
3FA21ADC148AABA900099D2F /* Build Support */,
3FA21AAA148AA85300099D2F /* Source */,
3FA21AAB148AA86600099D2F /* Private Headers */,
);
path = notifyd;
@ -443,7 +457,6 @@
3FA21A9E148AA7FA00099D2F /* notifyd.c */,
3FA21AA0148AA7FA00099D2F /* pathwatch.c */,
3FA21AA2148AA7FA00099D2F /* service.c */,
3FA21AA5148AA7FA00099D2F /* timer.c */,
);
name = Source;
sourceTree = "<group>";
@ -454,7 +467,6 @@
3FA21A9F148AA7FA00099D2F /* notifyd.h */,
3FA21AA1148AA7FA00099D2F /* pathwatch.h */,
3FA21AA3148AA7FA00099D2F /* service.h */,
3FA21AA6148AA7FA00099D2F /* timer.h */,
);
name = "Private Headers";
sourceTree = "<group>";
@ -492,7 +504,12 @@
727C902F1B9A372700D5B754 /* tests */ = {
isa = PBXGroup;
children = (
1864C88123F216CB00A383C1 /* notify_exec.c */,
1864C88223F2174E00A383C1 /* notify_test_helper.c */,
C0FF3C23234516ED00ABBA89 /* notify_matching.c */,
18383C6720F909A300D5C465 /* notify_regenerate.c */,
1846D85A24AAC578001D052D /* notify_register_file_desc.c */,
18177A7123612EDC008CCFDE /* notify_register_mach_port.c */,
18383C6820F909A300D5C465 /* notify_register_signal.c */,
18383C6920F909A300D5C465 /* notify_sigusr.c */,
727C90341B9A372700D5B754 /* Makefile */,
@ -501,10 +518,12 @@
727C90381B9A372700D5B754 /* notify_control.c */,
727C90391B9A372700D5B754 /* notify_disable_test.c */,
727C903A1B9A372700D5B754 /* notify_many_dups.c */,
183F91E32357FCEB009A1BFC /* notify_suspend.c */,
727C903B1B9A372700D5B754 /* random_test.c */,
9487BF2820882DAD0043BF74 /* parallel_register_cancel.c */,
182FD2C2210BB0F600BF263C /* notify_benchmark.c */,
180F8D3E21136EBF009A472B /* notify_pathwatch.c */,
C0FF3C242345172700ABBA89 /* notify_leaks.c */,
);
path = tests;
sourceTree = "<group>";
@ -564,6 +583,7 @@
buildToolPath = /usr/bin/make;
buildWorkingDirectory = tests;
dependencies = (
1864C88D23F227C200A383C1 /* PBXTargetDependency */,
);
name = darwintests;
passBuildSettingsInEnvironment = 1;
@ -572,6 +592,21 @@
/* End PBXLegacyTarget section */
/* Begin PBXNativeTarget section */
1864C88323F227AC00A383C1 /* notify_test_helper */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1864C88823F227AC00A383C1 /* Build configuration list for PBXNativeTarget "notify_test_helper" */;
buildPhases = (
1864C88423F227AC00A383C1 /* Sources */,
);
buildRules = (
);
dependencies = (
);
name = notify_test_helper;
productName = notifyutil;
productReference = 1864C88B23F227AC00A383C1 /* notify_test_helper */;
productType = "com.apple.product-type.tool";
};
1886390E20E1A22F00C8BEA9 /* notifybench */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1886391420E1A22F00C8BEA9 /* Build configuration list for PBXNativeTarget "notifybench" */;
@ -652,7 +687,6 @@
D289987405E68DCB004EDB86 /* Frameworks */,
2D38AA09102CD87C00D3D622 /* Copy man3 Files */,
3F947782191C324900A93E8E /* No Simulator Man Pages */,
3F947783191C327700A93E8E /* Sim compat symlink */,
);
buildRules = (
);
@ -669,6 +703,7 @@
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1100;
TargetAttributes = {
72FA84F61BD6E9DF00A4CC6F = {
CreatedOnToolsVersion = 7.1;
@ -683,13 +718,10 @@
};
buildConfigurationList = 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "Libnotify" */;
compatibilityVersion = "Xcode 3.1";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
en,
);
mainGroup = 08FB7794FE84155DC02AAC07 /* Libnotify */;
productRefGroup = 08FB7794FE84155DC02AAC07 /* Libnotify */;
@ -700,6 +732,7 @@
3FA21AAF148AA8E300099D2F /* notifyd */,
3FA21ABD148AA8F000099D2F /* notifyutil */,
1886390E20E1A22F00C8BEA9 /* notifybench */,
1864C88323F227AC00A383C1 /* notify_test_helper */,
3FA21AC7148AA93000099D2F /* cli_apps */,
72FA84F61BD6E9DF00A4CC6F /* darwintests */,
72FA84FB1BD6EAB900A4CC6F /* tests */,
@ -734,21 +767,6 @@
shellPath = "/bin/bash -e -x";
shellScript = "exec \"${SCRIPT_INPUT_FILE_0}\"";
};
3F947783191C327700A93E8E /* Sim compat symlink */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
files = (
);
inputPaths = (
"$(SRCROOT)/xcodescripts/sim-compat-symlink.sh",
);
name = "Sim compat symlink";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = "/bin/bash -e -x";
shellScript = "exec \"${SCRIPT_INPUT_FILE_0}\"";
};
3F947784191C32DC00A93E8E /* No Simulator Man Pages */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
@ -782,6 +800,14 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
1864C88423F227AC00A383C1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1864C88E23F227D200A383C1 /* notify_test_helper.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
1886390F20E1A22F00C8BEA9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -802,7 +828,6 @@
9456B8522023CAB300CF7D27 /* libnotify.c in Sources */,
3FA21AD1148AAA5000099D2F /* pathwatch.c in Sources */,
3FA21AD2148AAA5000099D2F /* service.c in Sources */,
3FA21AD3148AAA5000099D2F /* timer.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -841,6 +866,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
1864C88D23F227C200A383C1 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 1864C88323F227AC00A383C1 /* notify_test_helper */;
targetProxy = 1864C88C23F227C200A383C1 /* PBXContainerItemProxy */;
};
3FA21ACB148AA94A00099D2F /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 3FA21AAF148AA8E300099D2F /* notifyd */;
@ -859,10 +889,31 @@
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
1864C88923F227AC00A383C1 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 3F999964185C474E00EAD3A0 /* notifyutil.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
INSTALL_PATH = /AppleInternal/Tests/Libnotify/;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
1864C88A23F227AC00A383C1 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 3F999964185C474E00EAD3A0 /* notifyutil.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
INSTALL_PATH = /AppleInternal/Tests/Libnotify/;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
1886391520E1A22F00C8BEA9 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 3F999964185C474E00EAD3A0 /* notifyutil.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
INSTALL_PATH = /usr/local/bin;
PRODUCT_NAME = "$(TARGET_NAME)";
};
@ -872,6 +923,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 3F999964185C474E00EAD3A0 /* notifyutil.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
INSTALL_PATH = /usr/local/bin;
PRODUCT_NAME = "$(TARGET_NAME)";
};
@ -881,6 +933,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 3F82235D12B18551005DD509 /* libnotify.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
};
name = Release;
};
@ -888,18 +941,41 @@
isa = XCBuildConfiguration;
buildSettings = {
APPLY_RULES_IN_COPY_FILES = "";
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_ASSIGN_ENUM = 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_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_NULLABLE_TO_NONNULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LLVM_LTO = YES;
RUN_CLANG_STATIC_ANALYZER = YES;
SUPPORTED_PLATFORMS = "watchsimulator watchos iphonesimulator iphoneos macosx bridgeos appletvsimulator appletvos";
WARNING_CFLAGS = "-Wno-dollar-in-identifier-extension";
};
@ -909,6 +985,8 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 3F999963185C474E00EAD3A0 /* notifyd.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_ENTITLEMENTS = notifyd/entitlements.plist;
OTHER_CFLAGS = (
"$(inherited)",
"-DSINGLE_THREADED_NOTIFY_STATE=1",
@ -921,6 +999,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 3F999964185C474E00EAD3A0 /* notifyutil.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_ENTITLEMENTS = notifyutil/notifyutil_entitlements.plist;
PRODUCT_NAME = "$(TARGET_NAME)";
};
@ -930,6 +1009,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 3F999961185C474E00EAD3A0 /* base.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
@ -937,6 +1017,7 @@
72FA84F81BD6E9DF00A4CC6F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
COPY_PHASE_STRIP = "";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
@ -947,6 +1028,7 @@
72FA84FD1BD6EAB900A4CC6F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos";
};
@ -1017,18 +1099,43 @@
isa = XCBuildConfiguration;
buildSettings = {
APPLY_RULES_IN_COPY_FILES = "";
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_ASSIGN_ENUM = 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_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_NULLABLE_TO_NONNULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LLVM_LTO = YES;
ONLY_ACTIVE_ARCH = YES;
RUN_CLANG_STATIC_ANALYZER = YES;
SUPPORTED_PLATFORMS = "watchsimulator watchos iphonesimulator iphoneos macosx bridgeos appletvsimulator appletvos";
WARNING_CFLAGS = "-Wno-dollar-in-identifier-extension";
};
@ -1038,6 +1145,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 3F82235D12B18551005DD509 /* libnotify.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
};
name = Debug;
};
@ -1045,6 +1153,8 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 3F999963185C474E00EAD3A0 /* notifyd.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_ENTITLEMENTS = notifyd/entitlements.plist;
OTHER_CFLAGS = (
"$(inherited)",
"-DSINGLE_THREADED_NOTIFY_STATE=1",
@ -1057,6 +1167,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 3F999964185C474E00EAD3A0 /* notifyutil.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_ENTITLEMENTS = notifyutil/notifyutil_entitlements.plist;
PRODUCT_NAME = "$(TARGET_NAME)";
};
@ -1066,6 +1177,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 3F999961185C474E00EAD3A0 /* base.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
@ -1073,6 +1185,7 @@
949B7842208BE1F2002AD6AF /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
COPY_PHASE_STRIP = "";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
@ -1083,6 +1196,7 @@
949B7843208BE1F2002AD6AF /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos";
};
@ -1154,6 +1268,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1864C88823F227AC00A383C1 /* Build configuration list for PBXNativeTarget "notify_test_helper" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1864C88923F227AC00A383C1 /* Release */,
1864C88A23F227AC00A383C1 /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1886391420E1A22F00C8BEA9 /* Build configuration list for PBXNativeTarget "notifybench" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -39,8 +39,6 @@
</BuildableReference>
</TestableReference>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@ -52,8 +50,6 @@
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

View File

@ -145,6 +145,7 @@ _notify_lib_notify_state_init(notify_state_t * ns, uint32_t flags)
_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_init_64(&ns->event_table, offsetof(event_data_t, event_token));
}
// We only need to lock in the client
@ -170,6 +171,8 @@ _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);
n->refcount++;
/* detect duplicates - should never happen, but it would be bad */
c = _nc_table_find_64(&ns->client_table, cid);
if (c != NULL) return NULL;
@ -182,7 +185,6 @@ _internal_client_new(notify_state_t *ns, pid_t pid, int token, name_info_t *n)
c->name_info = n;
LIST_INSERT_HEAD(&n->subscriptions, c, client_subscription_entry);
n->refcount++;
_nc_table_insert_64(&ns->client_table, &c->cid.hash_key);
return c;
@ -193,9 +195,9 @@ _internal_client_release(notify_state_t *ns, client_t *c)
{
_nc_table_delete_64(&ns->client_table, c->cid.hash_key);
if (c->state_and_type & NOTIFY_TYPE_FILE) {
if (notify_is_type(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) {
} else if (notify_is_type(c->state_and_type, NOTIFY_TYPE_PORT)) {
/* release my send right to the port */
mach_port_deallocate(mach_task_self(), c->deliver.port);
}
@ -238,7 +240,7 @@ _internal_new_name(notify_state_t *ns, const char *name)
static void
_internal_insert_controlled_name(notify_state_t *ns, name_info_t *n)
{
int i, j;
uint32_t i, j;
if (n == NULL) return;
@ -361,6 +363,72 @@ _notify_lib_check_controlled_access(notify_state_t *ns, char *name, uid_t uid, g
return status;
}
static inline uint32_t
_internal_send_port(notify_state_t *ns, client_t *c, port_data_t *port_data, mach_port_t port)
{
kern_return_t kstatus;
mach_msg_empty_send_t msg;
mach_msg_option_t opts = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
if (port_data == NULL)
{
port_data = _nc_table_find_n(&ns->port_table, port);
}
if ((port_data != NULL) && (port_data->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED))
{
c->suspend_count++;
c->state_and_type |= NOTIFY_CLIENT_STATE_SUSPENDED;
c->state_and_type |= NOTIFY_CLIENT_STATE_PENDING;
return NOTIFY_STATUS_OK;
}
if (ns->flags & NOTIFY_STATE_ENABLE_RESEND) opts |= MACH_SEND_NOTIFY;
memset(&msg, 0, sizeof(mach_msg_empty_send_t));
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 = port;
msg.header.msgh_id = (mach_msg_id_t)c->cid.token;
kstatus = mach_msg(&msg.header, opts, msg.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (kstatus == MACH_SEND_TIMED_OUT)
{
/* deallocate port rights obtained via pseudo-receive after failed mach_msg() send */
mach_msg_destroy(&msg.header);
if (ns->flags & NOTIFY_STATE_ENABLE_RESEND)
{
/*
* Suspend on timeout.
* notifyd will get a MACH_NOTIFY_SEND_POSSIBLE and trigger a retry.
* c->suspend_count must be zero, or we would not be trying to send.
*/
c->suspend_count++;
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_MACH_MSG_TIMEOUT;
}
else if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_MACH_MSG_FAILED;
c->state_and_type &= ~NOTIFY_CLIENT_STATE_PENDING;
c->state_and_type &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
return NOTIFY_STATUS_OK;
}
/*
* Send notification to a subscriber
*/
@ -368,7 +436,6 @@ static uint32_t
_internal_send(notify_state_t *ns, client_t *c,
proc_data_t *proc_data, port_data_t *port_data)
{
uint32_t send;
if (c->state_and_type & NOTIFY_CLIENT_STATE_SUSPENDED)
{
@ -388,9 +455,7 @@ _internal_send(notify_state_t *ns, client_t *c,
return NOTIFY_STATUS_OK;
}
send = c->cid.token;
switch (c->state_and_type & NOTIFY_TYPE_MASK)
switch (notify_get_type(c->state_and_type))
{
case NOTIFY_TYPE_SIGNAL:
{
@ -413,8 +478,8 @@ _internal_send(notify_state_t *ns, client_t *c,
if (c->deliver.fd >= 0)
{
send = htonl(send);
len = write(c->deliver.fd, &send, sizeof(uint32_t));
uint32_t send_value = htonl(c->cid.token);
len = write(c->deliver.fd, &send_value, sizeof(uint32_t));
if (len != sizeof(uint32_t))
{
close(c->deliver.fd);
@ -431,62 +496,24 @@ _internal_send(notify_state_t *ns, client_t *c,
case NOTIFY_TYPE_PORT:
{
kern_return_t kstatus;
mach_msg_empty_send_t msg;
mach_msg_option_t opts = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
return _internal_send_port(ns, c, port_data, c->deliver.port);
}
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_and_type |= NOTIFY_CLIENT_STATE_SUSPENDED;
c->state_and_type |= NOTIFY_CLIENT_STATE_PENDING;
return NOTIFY_STATUS_OK;
case NOTIFY_TYPE_XPC_EVENT:
{
xpc_object_t payload = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(payload, NOTIFY_XPC_EVENT_PAYLOAD_KEY_NAME, c->name_info->name);
name_info_t *n = _nc_table_find_64(&ns->name_id_table, c->name_info->name_id);
if (n != NULL) {
xpc_dictionary_set_uint64(payload, NOTIFY_XPC_EVENT_PAYLOAD_KEY_STATE, n->state);
}
if (ns->flags & NOTIFY_STATE_ENABLE_RESEND) opts |= MACH_SEND_NOTIFY;
memset(&msg, 0, sizeof(mach_msg_empty_send_t));
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->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);
if (kstatus == MACH_SEND_TIMED_OUT)
{
/* deallocate port rights obtained via pseudo-receive after failed mach_msg() send */
mach_msg_destroy(&msg.header);
if (ns->flags & NOTIFY_STATE_ENABLE_RESEND)
{
/*
* Suspend on timeout.
* notifyd will get a MACH_NOTIFY_SEND_POSSIBLE and trigger a retry.
* c->suspend_count must be zero, or we would not be trying to send.
*/
c->suspend_count++;
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_MACH_MSG_TIMEOUT;
int rc = xpc_event_publisher_fire_noboost(ns->event_publisher, c->deliver.event_token, payload);
xpc_release(payload);
if (rc != 0) {
return NOTIFY_STATUS_TOKEN_FIRE_FAILED;
}
else if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_MACH_MSG_FAILED;
c->state_and_type &= ~NOTIFY_CLIENT_STATE_PENDING;
c->state_and_type &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
@ -494,6 +521,15 @@ _internal_send(notify_state_t *ns, client_t *c,
return NOTIFY_STATUS_OK;
}
case NOTIFY_TYPE_COMMON_PORT:
{
if (!proc_data || !proc_data->common_port_data)
{
return NOTIFY_STATUS_OK;
}
return _internal_send_port(ns, c, port_data, proc_data->common_port_data->port);
}
default:
{
break;
@ -845,7 +881,6 @@ _internal_register_common(notify_state_t *ns, const char *name, pid_t pid, int t
{
client_t *c;
name_info_t *n;
int is_new_name;
uint32_t status;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
@ -855,13 +890,10 @@ _internal_register_common(notify_state_t *ns, const char *name, pid_t pid, int t
if (status != NOTIFY_STATUS_OK) return NOTIFY_STATUS_NOT_AUTHORIZED;
*outc = NULL;
is_new_name = 0;
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_NEW_NAME_FAILED;
}
@ -869,13 +901,7 @@ _internal_register_common(notify_state_t *ns, const char *name, pid_t pid, int t
c = _internal_client_new(ns, pid, token, n);
if (c == NULL)
{
if (is_new_name == 1)
{
_nc_table_delete(&ns->name_table, n->name);
free(n);
ns->stat_name_free++;
}
_internal_release_name_info(ns, n);
return NOTIFY_STATUS_NEW_CLIENT_FAILED;
}
@ -886,7 +912,6 @@ _internal_register_common(notify_state_t *ns, const char *name, pid_t pid, int t
/*
* Register for signal.
* Returns the client_id;
*/
uint32_t
_notify_lib_register_signal(notify_state_t *ns, const char *name, pid_t pid, int token, uint32_t sig, uid_t uid, gid_t gid, uint64_t *out_nid)
@ -919,7 +944,6 @@ _notify_lib_register_signal(notify_state_t *ns, const char *name, pid_t pid, int
/*
* Register for notification on a file descriptor.
* Returns the client_id;
*/
uint32_t
_notify_lib_register_file_descriptor(notify_state_t *ns, const char *name, pid_t pid, int token, int fd, uid_t uid, gid_t gid, uint64_t *out_nid)
@ -952,7 +976,6 @@ _notify_lib_register_file_descriptor(notify_state_t *ns, const char *name, pid_t
/*
* Register for notification on a mach port.
* Returns the client_id;
*/
uint32_t
_notify_lib_register_mach_port(notify_state_t *ns, const char *name, pid_t pid, int token, mach_port_t port, uid_t uid, gid_t gid, uint64_t *out_nid)
@ -985,7 +1008,6 @@ _notify_lib_register_mach_port(notify_state_t *ns, const char *name, pid_t pid,
/*
* Plain registration - only for notify_check()
* Returns the client_id.
*/
uint32_t
_notify_lib_register_plain(notify_state_t *ns, const char *name, pid_t pid, int token, uint32_t slot, uint32_t uid, uint32_t gid, uint64_t *out_nid)
@ -1024,6 +1046,67 @@ _notify_lib_register_plain(notify_state_t *ns, const char *name, pid_t pid, int
return NOTIFY_STATUS_OK;
}
/*
* Register for an XPC event notification
*/
uint32_t
_notify_lib_register_xpc_event(notify_state_t *ns, const char *name, pid_t pid, int token, uint64_t event_token, uid_t uid, gid_t gid, uint64_t *out_nid)
{
client_t *c = NULL;
uint32_t status;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
_notify_state_lock(&ns->lock);
assert(ns->event_publisher != NULL);
status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
if (status != NOTIFY_STATUS_OK)
{
_notify_state_unlock(&ns->lock);
return status;
}
c->state_and_type &= ~NOTIFY_TYPE_MASK;
c->state_and_type |= NOTIFY_TYPE_XPC_EVENT;
c->deliver.event_token = event_token;
*out_nid = c->name_info->name_id;
_notify_state_unlock(&ns->lock);
return NOTIFY_STATUS_OK;
}
/*
* Register for notification on the common port.
*/
uint32_t
_notify_lib_register_common_port(notify_state_t *ns, const char *name, pid_t pid, int token, uid_t uid, gid_t gid, uint64_t *out_nid)
{
client_t *c;
uint32_t status;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
c = NULL;
_notify_state_lock(&ns->lock);
status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
if (status != NOTIFY_STATUS_OK)
{
_notify_state_unlock(&ns->lock);
return status;
}
c->state_and_type &= ~NOTIFY_TYPE_MASK;
c->state_and_type |= NOTIFY_TYPE_COMMON_PORT;
*out_nid = c->name_info->name_id;
_notify_state_unlock(&ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_set_owner(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
{

View File

@ -29,6 +29,7 @@
#include <pthread.h>
#include <mach/mach.h>
#include <dispatch/dispatch.h>
#include <xpc/private.h>
#include "table.h"
#include <TargetConditionals.h>
@ -51,22 +52,29 @@ extern const char *_notify_shm_id(void);
#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_TYPE_PORT 0x00000003
#define NOTIFY_TYPE_FILE 0x00000004
#define NOTIFY_TYPE_SIGNAL 0x00000005
#define NOTIFY_TYPE_XPC_EVENT 0x00000006
#define NOTIFY_TYPE_COMMON_PORT 0x00000007
#define NOTIFY_TYPE_MASK 0x0000000f // If this is changed, make sure it doesn't muck with NOTIFY_CLIENT_STATE_*
#define N_NOTIFY_TYPES 8
#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_COALESCE_BASE 0x08000000
#define NOTIFY_FLAG_COALESCED 0x04000000
#define NOTIFY_FLAG_RETAINED 0x02000000
#define NOTIFY_FLAG_CANCELED 0x01000000
#define NOTIFY_FLAG_SUSPENDED 0x00800000
#define NOTIFY_FLAG_DEFERRED_POST 0x00400000
/* 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)
#define notify_get_type(flags) ((flags) & NOTIFY_TYPE_MASK)
#define notify_is_type(flags, type) (notify_get_type(flags) == (type))
/* Return values for notify_check() */
#define NOTIFY_CHECK_FALSE 0
@ -101,21 +109,25 @@ extern const char *_notify_shm_id(void);
/* port_data_t::flags and proc_data_t::flags */
#define PORT_PROC_FLAGS_NONE 0x00000000
#define NOTIFY_PORT_PROC_STATE_SUSPENDED 0x00000001
#define NOTIFY_PORT_FLAG_COMMON 0x00000002
#define NOTIFY_PORT_FLAG_COMMON_READY_TO_FREE 0x00000004
/* notify state flags */
#define NOTIFY_STATE_USE_LOCKS 0x00000001
#define NOTIFY_STATE_ENABLE_RESEND 0x00000002
#define NOTIFY_XPC_EVENT_PAYLOAD_KEY_NAME "Notification"
#define NOTIFY_XPC_EVENT_PAYLOAD_KEY_STATE "_State"
#define NOTIFY_CLIENT_SELF 0
#define SIGNAL_NONE -1
#define FD_NONE -1
#define SLOT_NONE -1
#define SLOT_NONE (uint32_t)~0
typedef struct
{
LIST_HEAD(, client_s) subscriptions;
char *name;
void *private;
uint64_t name_id;
uint64_t state;
uint64_t state_time;
@ -134,6 +146,7 @@ typedef union client_delivery_u
int fd;
mach_port_t port;
uint32_t sig;
uint64_t event_token;
} client_delivery_t;
typedef struct client_s
@ -159,7 +172,7 @@ typedef struct client_s
typedef struct
{
LIST_HEAD(, client_s) clients;
uint32_t port;
mach_port_t port;
uint32_t flags;
} port_data_t;
@ -169,8 +182,15 @@ typedef struct
dispatch_source_t src;
uint32_t pid;
uint32_t flags;
port_data_t *common_port_data;
} proc_data_t;
typedef struct
{
client_t *client;
uint64_t event_token;
} event_data_t;
typedef struct
{
/* last allocated name id */
@ -180,7 +200,9 @@ typedef struct
table_64_t client_table;
table_n_t port_table;
table_n_t proc_table;
table_64_t event_table;
name_info_t **controlled_name;
xpc_event_publisher_t event_publisher;
uint32_t flags;
uint32_t controlled_name_count;
os_unfair_lock lock;
@ -207,6 +229,8 @@ uint32_t _notify_lib_register_plain(notify_state_t *ns, const char *name, pid_t
uint32_t _notify_lib_register_signal(notify_state_t *ns, const char *name, pid_t pid, int token, uint32_t sig, uint32_t uid, uint32_t gid, uint64_t *out_nid);
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_register_xpc_event(notify_state_t *ns, const char *name, pid_t pid, int token, uint64_t event_token, uid_t uid, gid_t gid, uint64_t *out_nid);
uint32_t _notify_lib_register_common_port(notify_state_t *ns, const char *name, pid_t pid, int token, uid_t uid, gid_t gid, uint64_t *out_nid);
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);

View File

@ -46,7 +46,7 @@
.Fn notify_post "const char *name"
.Ft uint32_t
.Fn notify_register_check "const char *name, int *out_token"
.Ft void
.Ft typedef 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"
@ -317,10 +317,10 @@ name convention used for Java package names and for System Preferences
on Mac OS X.
For example, "com.mydomain.example.event".
.Pp
Apple reserves the portion
of the namespace prefixed by "com.apple.".
This policy is not
enforced in the current implementation, but may be in the future.
Apple reserves the portion of the namespace prefixed by "com.apple.".
This policy is not enforced in the current implementation, but may be in the
future. It is enforced that the portion of the namespace prefixed by
"com.apple.system." is reserved for root system process.
.Pp
Names in the space "user.uid.UID", where UID is a numeric user ID number
are reserved for processes with that UID.

View File

@ -2,7 +2,7 @@
* Copyright (c) 2003-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
*
* Portions Copyright (c) 2003-2010 Apple Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
@ -11,7 +11,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,
@ -19,7 +19,7 @@
* 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@
*/
@ -64,12 +64,12 @@
* for a name in rapid succession may result in a single notification sent
* to clients registered for notification for that name. Clients checking
* for changes using the notify_check() routine cannot determine if
* more than one event pas been posted since a previous call to
* more than one event pas been posted since a previous call to
* notify_check() for that name.
*
* "False positives" may occur in notify_check() when used with a token
* generated by notify_register_check() due to implementation constraints.
* This behavior may vary in future releases.
* This behavior may vary in future releases.
*
* Synchronization between two processes may be achieved using the
* notify_set_state() and notify_get_state() routines.
@ -123,7 +123,7 @@ OS_EXPORT uint32_t notify_post(const char *name);
typedef void (^notify_handler_t)(int token);
/*!
* @function notify_register
* @function notify_register_dispatch
* @abstract Request notification delivery to a dispatch queue.
* @discussion When notifications are received by the process, the notify
* subsystem will deliver the registered Block to the target
@ -163,7 +163,7 @@ OS_EXPORT uint32_t notify_register_check(const char *name, int *out_token);
* Request notification delivery by UNIX signal.
*
* A client may request signal notification for multiple names. After a signal
* is delivered, the notify_check() routine may be called with each notification
* is delivered, the notify_check() routine may be called with each notification
* token to determine which name (if any) generated the signal notification.
*
* @param name (input) notification name
@ -174,11 +174,11 @@ OS_EXPORT uint32_t notify_register_check(const char *name, int *out_token);
OS_EXPORT uint32_t notify_register_signal(const char *name, int sig, int *out_token);
/*!
* Request notification by mach message.
* Request notification by mach message.
*
* Notifications are delivered by an empty message sent to a mach port.
* By default, a new port is allocated and a pointer to it is returned
* as the value of "notify_port". A mach port previously returned by a
* as the value of "notify_port". A mach port previously returned by a
* call to this routine may be used for notifications if a pointer to that
* port is passed in to the routine and NOTIFY_REUSE is set in the flags
* parameter. The notification service must be able to extract send
@ -206,14 +206,14 @@ OS_EXPORT uint32_t notify_register_signal(const char *name, int sig, int *out_to
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.
* Request notification by a write to a file descriptor.
*
* Notifications are delivered by a write to a file descriptor.
* By default, a new file descriptor is created and a pointer to it
* is returned as the value of "notify_fd". 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.
*
* Note that the kernel limits the buffer space for queued writes on a
* file descriptor. If it is important that notifications should not be
@ -256,8 +256,8 @@ OS_EXPORT uint32_t notify_check(int token, int *check);
/*!
* Cancel notification and free resources associated with a notification
* token. Mach ports and file descriptor associated with a token are released
* (deallocated or closed) when all registration tokens associated with
* token. Mach ports and file descriptor associated with a token are released
* (deallocated or closed) when all registration tokens associated with
* the port or file descriptor have been cancelled.
*
* @param token
@ -296,7 +296,7 @@ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0);
/*!
* Set or get a state value associated with a notification token.
* Each key in the notification namespace has an associated integer value available
* for use by clients as for application-specific purposes. A common usage is to
* for use by clients as for application-specific purposes. A common usage is to
* allow two processes or threads to synchronize their activities. For example, a
* server process may need send a notification when a resource becomes available.
* A client process can register for the notification, but when it starts up it will

File diff suppressed because it is too large Load Diff

View File

@ -21,28 +21,18 @@
* @APPLE_LICENSE_HEADER_END@
*/
#define __OS_EXPOSE_INTERNALS__ 1
#include <os/internal/internal_shared.h>
#include <os/atomic_private.h>
#include <os/crashlog_private.h>
#include <os/lock.h>
#include <dispatch/dispatch.h>
#include <mach/mach.h>
#include <os/lock.h>
#include <stdatomic.h>
#include <stdint.h>
#include <TargetConditionals.h>
#include "libnotify.h"
#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_INTERNAL_CRASH(c, x) OS_BUG_INTERNAL(c, "LIBNOTIFY", x)
#define NOTIFY_CLIENT_CRASH(c, x) OS_BUG_CLIENT(c, "LIBNOTIFY", x)
#define NOTIFY_STATUS_SERVER_CHECKIN_FAILED 11
// was NOTIFY_STATUS_LIB_SELF_STATE_FAILED 12
@ -88,17 +78,24 @@
#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
// No longer used: NOTIFY_STATUS_TIMER_FAILED 55
#define NOTIFY_STATUS_DOUBLE_REG 56
#define NOTIFY_STATUS_NO_REGEN_NEEDED 57
#define NOTIFY_STATUS_TOKEN_FIRE_FAILED 58
#define NOTIFY_STATUS_INVALID_PORT_INTERNAL 59
#define NOTIFY_STATUS_NO_NID 60
#define IS_INTERNAL_ERROR(X) (X >= 11)
#define USER_PROTECTED_UID_PREFIX "user.uid."
#define USER_PROTECTED_UID_PREFIX_LEN 9
#define CANARY_COUNT 13
struct notify_globals_s
{
uint64_t canary[CANARY_COUNT];
/* global lock */
os_unfair_lock notify_lock;

View File

@ -259,3 +259,29 @@ routine _notify_server_dump
fileport : mach_port_move_send_t;
ServerAuditToken audit : audit_token_t
);
routine _notify_generate_common_port
(
server : mach_port_t;
out status : uint32_t;
out port : mach_port_move_receive_t;
ServerAuditToken audit : audit_token_t
);
simpleroutine _notify_server_register_common_port
(
server : mach_port_t;
name : notify_name;
token: int;
ServerAuditToken audit : audit_token_t
);
routine _notify_server_register_mach_port_3
(
server : mach_port_t;
name : notify_name;
token: int;
out status : uint32_t;
out port : mach_port_move_receive_t;
ServerAuditToken audit : audit_token_t
);

View File

@ -56,8 +56,12 @@ __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);
// This SPI requires a sandbox exception in notifyd and will fail silently for
// new clients or new filepaths from existing clients. It is reccomended that
// both existing and new clients use some other file monitoring system, such as
// dispatch_source or FSEvents.
OS_EXPORT uint32_t notify_monitor_file(int token, char *path, int flags)
__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3);
__API_DEPRECATED("No longer supported for new clients", macos(10.7, 10.16), ios(4.3, 14.0), watchos(1.0, 7.0), tvos(1.0, 14.0));
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));

View File

@ -1,16 +0,0 @@
// fake DTrace probes for libnotify
#define NOTIFY_REGISTER_MACH_PORT(...) (0)
#define NOTIFY_REGISTER_MACH_PORT_ENABLED(...) (0)
#define NOTIFY_POST(...) (0)
#define NOTIFY_POST_ENABLED(...) (0)
#define NOTIFY_CHECK(...) (0)
#define NOTIFY_CHECK_ENABLED(...) (0)
#define NOTIFY_DELIVER_START(...) (0)
#define NOTIFY_DELIVER_START_ENABLED(...) (0)
#define NOTIFY_DELIVER_END(...) (0)
#define NOTIFY_DELIVER_END_ENABLED(...) (0)

View File

@ -117,7 +117,6 @@ main(int argc, char *argv[])
{
uint32_t r;
kern_return_t kr;
unsigned i, j;
kr = mach_timebase_info(&tbi);
assert(!kr);
@ -140,7 +139,7 @@ main(int argc, char *argv[])
dispatch_queue_t disp_q = dispatch_queue_create("Notify.Test", NULL);
for (i = 1; i < argc; i++)
for (int 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;
@ -149,12 +148,12 @@ main(int argc, char *argv[])
if (cnt > MAX_CNT) cnt = MAX_CNT;
if (spl > MAX_SPL) spl = MAX_SPL + 1;
for (j = 0 ; j < spl; j++)
for (uint32_t j = 0 ; j < spl; j++)
{
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = asprintf(&n[i], "dummy.test.%d", i);
assert(r != -1);
assert(r != (uint32_t)~0);
l[i] = strlen(n[i]);
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &p[i]);
assert(kr == 0);
@ -162,7 +161,7 @@ main(int argc, char *argv[])
/* Empty Loop */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
spin++;
}
@ -170,7 +169,7 @@ main(int argc, char *argv[])
#ifdef NO_OP_TESTS
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_no_op_str_sync(n[i], l[i]);
assert(r == 0);
@ -178,7 +177,7 @@ main(int argc, char *argv[])
nss[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_no_op_str_async(n[i], l[i]);
assert(r == 0);
@ -186,7 +185,7 @@ main(int argc, char *argv[])
nsa[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_no_op_int_sync(i);
assert(r == 0);
@ -208,7 +207,7 @@ main(int argc, char *argv[])
/* Register Plain */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_register_plain(n[i], &t[i]);
assert(r == 0);
@ -217,7 +216,7 @@ main(int argc, char *argv[])
/* Post 1 */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_post(n[i]);
assert(r == 0);
@ -226,7 +225,7 @@ main(int argc, char *argv[])
/* Post 2 */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_post(n[i]);
assert(r == 0);
@ -235,7 +234,7 @@ main(int argc, char *argv[])
/* Post 3 */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_post(n[i]);
assert(r == 0);
@ -244,7 +243,7 @@ main(int argc, char *argv[])
/* Cancel Plain */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_cancel(t[i]);
assert(r == 0);
@ -253,7 +252,7 @@ main(int argc, char *argv[])
/* Register Mach Port */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_register_mach_port(n[i], &p[i], NOTIFY_REUSE, &t[i]);
assert(r == 0);
@ -263,7 +262,7 @@ main(int argc, char *argv[])
/* Set State 1 */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_set_state(t[i], 1);
assert(r == 0);
@ -272,7 +271,7 @@ main(int argc, char *argv[])
/* Get State */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
uint64_t dummy;
r = notify_get_state(t[i], &dummy);
@ -282,7 +281,7 @@ main(int argc, char *argv[])
/* Set State 2 */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_set_state(t[i], 2);
assert(r == 0);
@ -291,7 +290,7 @@ main(int argc, char *argv[])
/* Cancel Port */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_cancel(t[i]);
assert(r == 0);
@ -302,7 +301,7 @@ main(int argc, char *argv[])
/* Register Check */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_register_check("com.apple.notify.test.check", &t[i]);
assert(r == 0);
@ -311,7 +310,7 @@ main(int argc, char *argv[])
/* Check 1 */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_check(t[i], &check);
assert(r == 0);
@ -321,7 +320,7 @@ main(int argc, char *argv[])
/* Check 2 */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_check(t[i], &check);
assert(r == 0);
@ -331,7 +330,7 @@ main(int argc, char *argv[])
/* Check 3 */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_check(t[i], &check);
assert(r == 0);
@ -345,7 +344,7 @@ main(int argc, char *argv[])
/* Check 4 */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_check(t[i], &check);
assert(r == 0);
@ -355,7 +354,7 @@ main(int argc, char *argv[])
/* Check 5 */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_check(t[i], &check);
assert(r == 0);
@ -365,7 +364,7 @@ main(int argc, char *argv[])
/* Cancel Check */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_cancel(t[i]);
assert(r == 0);
@ -374,7 +373,7 @@ main(int argc, char *argv[])
/* Register Dispatch 1 */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_register_dispatch(n[i], &t[i], disp_q, ^(int x){
dispatch_changer = x;
@ -385,7 +384,7 @@ main(int argc, char *argv[])
/* Register Dispatch 2 (Coalesced) */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_register_dispatch(n[i], &t_2[i], disp_q, ^(int x){
dispatch_changer = x;
@ -397,7 +396,7 @@ main(int argc, char *argv[])
/* Cancel Dispatch */
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
r = notify_cancel(t[i]);
assert(r == 0);
@ -406,7 +405,7 @@ main(int argc, char *argv[])
}
cancel_disp[j] = mach_absolute_time() - s;
for (i = 0; i < cnt; i++)
for (uint32_t i = 0; i < cnt; i++)
{
free(n[i]);
kr = mach_port_mod_refs(mach_task_self(), p[i], MACH_PORT_RIGHT_RECEIVE, -1);

View File

@ -18,15 +18,12 @@
<key>com.apple.system.notification_center</key>
<true/>
</dict>
<key>PublishesEvents</key>
<string>com.apple.notifyd.matching</string>
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/notifyd</string>
</array>
<key>JetsamProperties</key>
<dict>
<key>JetsamPriority</key>
<integer>-1000</integer>
</dict>
<key>POSIXSpawnType</key>
<string>Interactive</string>
</dict>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>seatbelt-profiles</key>
<array>
<string>notifyd</string>
</array>
</dict>
</plist>

View File

@ -1 +0,0 @@
../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/notify.defs

View File

@ -1 +0,0 @@
../notify_ipc.defs

View File

@ -31,7 +31,9 @@
#include <mach/mach_error.h>
#include <mach/mach_traps.h>
#include <sys/sysctl.h>
#include <sys/kauth.h>
#include <pthread.h>
#include <pwd.h>
#include <sys/fcntl.h>
#include <dispatch/private.h>
#include <assert.h>
@ -43,6 +45,8 @@
#include "notifyServer.h"
#include <sandbox.h>
static inline void proc_cancel(proc_data_t *pdata);
static void
port_arm_mach_notifications(mach_port_t port)
{
@ -78,6 +82,24 @@ port_free(void *_pp)
free(pdata);
}
// This should be called either when the process cleans up, or when the process
// forgets about a common port, such as after an exec.
static void
common_port_free(port_data_t *common_port_data) {
// Common port registrations should not be freed until both the mach port cleanup and the process
// cleanup is done. Whichever happens first sets the _READY_TO_FREE flags, and whichever happens
// second frees.
if (common_port_data->flags & NOTIFY_PORT_FLAG_COMMON_READY_TO_FREE) {
if (!LIST_EMPTY(&common_port_data->clients)) {
NOTIFY_INTERNAL_CRASH(0, "port_proc still had clients");
}
log_message(ASL_LEVEL_DEBUG, "do_mach_notify_dead_name freed port %x\n", (unsigned int)common_port_data);
port_free(common_port_data);
} else {
common_port_data->flags |= NOTIFY_PORT_FLAG_COMMON_READY_TO_FREE;
}
}
static void
proc_free(void *_pp)
{
@ -92,13 +114,29 @@ proc_free(void *_pp)
ns->stat_portproc_free++;
if (pdata->common_port_data) {
common_port_free(pdata->common_port_data);
}
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_event(void *_pp)
{
proc_data_t *pdata = _pp;
proc_cancel(pdata);
}
static proc_data_t *
proc_create(notify_state_t *ns, client_t *c, pid_t pid)
{
dispatch_source_t src = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid,
DISPATCH_PROC_EXIT, global.workloop);
dispatch_source_set_event_handler_f(src, proc_event);
proc_data_t *pdata = malloc(sizeof(proc_data_t));
if (pdata == NULL) {
@ -112,18 +150,25 @@ proc_create(notify_state_t *ns, client_t *c, pid_t proc, dispatch_source_t src)
LIST_INIT(&pdata->clients);
pdata->src = src;
pdata->flags = PORT_PROC_FLAGS_NONE;
pdata->pid = proc;
pdata->pid = (uint32_t)pid;
pdata->common_port_data = NULL;
_nc_table_insert_n(&ns->proc_table, &pdata->pid);
LIST_INSERT_HEAD(&pdata->clients, c, client_pid_entry);
if(c) {
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);
return pdata;
}
static void
// Returns true on success
static bool
port_create(notify_state_t *ns, client_t *c, mach_port_t port)
{
kern_return_t kstatus;
port_data_t *pdata = calloc(1, sizeof(port_data_t));
if (pdata == NULL) {
@ -139,20 +184,57 @@ port_create(notify_state_t *ns, client_t *c, mach_port_t 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);
kstatus = mach_port_insert_right(mach_task_self(), port,
port, MACH_MSG_TYPE_COPY_SEND);
if (kstatus != KERN_SUCCESS) {
// This will fail if the port is dead
return false;
}
/* arming SEND_POSSIBLE must be done before we attempt any send */
port_arm_mach_notifications(port);
return true;
}
static bool
static port_data_t *
common_port_create(notify_state_t *ns, 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;
pdata->flags = NOTIFY_PORT_FLAG_COMMON;
_nc_table_insert_n(&ns->port_table, &pdata->port);
/* arming SEND_POSSIBLE must be done before we attempt any send */
port_arm_mach_notifications(port);
return pdata;
}
static proc_data_t *
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) {
if (pdata && c) {
LIST_INSERT_HEAD(&pdata->clients, c, client_pid_entry);
}
return pdata;
}
static void proc_add_client(proc_data_t *pdata,client_t *c, pid_t pid)
{
if (pdata && c) {
LIST_INSERT_HEAD(&pdata->clients, c, client_pid_entry);
}
return pdata != NULL;
}
static bool
@ -226,17 +308,12 @@ port_proc_cancel_client(client_t *c)
n = c->name_info;
assert(n != NULL);
if ((n->refcount == 1) && (n->private != NULL))
{
service_close(n->private);
n->private = NULL;
}
if (c->state_and_type & NOTIFY_TYPE_MEMORY)
if (notify_is_type(c->state_and_type, NOTIFY_TYPE_MEMORY))
{
global.shared_memory_refcount[n->slot]--;
}
else if (c->state_and_type & NOTIFY_TYPE_PORT)
else if (notify_is_type(c->state_and_type, NOTIFY_TYPE_PORT) || notify_is_type(c->state_and_type, NOTIFY_TYPE_COMMON_PORT))
{
LIST_REMOVE(c, client_port_entry);
}
@ -272,7 +349,19 @@ do_mach_notify_dead_name(mach_port_t notify, mach_port_name_t port)
LIST_FOREACH_SAFE(c, &pdata->clients, client_port_entry, tmp) {
port_proc_cancel_client(c);
}
port_free(pdata);
if (pdata->flags & NOTIFY_PORT_FLAG_COMMON) {
// Common port registrations should not be freed until both the mach port cleanup and the process
// cleanup is done. Whichever happens first sets the _READY_TO_FREE flags, and whichever happens
// second frees.
if (pdata->flags & NOTIFY_PORT_FLAG_COMMON_READY_TO_FREE) {
log_message(ASL_LEVEL_DEBUG, "do_mach_notify_dead_name freed port %x\n", (unsigned int)port);
port_free(pdata);
} else {
pdata->flags |= NOTIFY_PORT_FLAG_COMMON_READY_TO_FREE;
}
} else {
port_free(pdata);
}
// the act of receiving a dead name notification allocates a dead-name
// right that must be deallocated
@ -280,41 +369,63 @@ do_mach_notify_dead_name(mach_port_t notify, mach_port_name_t port)
return KERN_SUCCESS;
}
static void
proc_event(void *_pp)
{
proc_data_t *pdata = _pp;
proc_cancel(pdata);
}
static void
static proc_data_t *
register_proc(client_t *c, pid_t pid)
{
dispatch_source_t src;
if (pid <= 0) return NULL;
proc_data_t *result = NULL;
if (pid <= 0) return;
if (proc_register(&global.notify_state, c, pid)) {
return;
if ((result = proc_register(&global.notify_state, c, pid)) != NULL) {
return result;
}
src = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid,
DISPATCH_PROC_EXIT, global.workloop);
dispatch_source_set_event_handler_f(src, proc_event);
proc_create(&global.notify_state, c, pid, src);
return proc_create(&global.notify_state, c, pid);
}
static void
// Returns true on success
static bool
register_port(client_t *c, mach_port_t port)
{
if (port_register(&global.notify_state, c, port)) {
return;
return true;
}
port_create(&global.notify_state, c, port);
return port_create(&global.notify_state, c, port);
}
static void
register_xpc_event(client_t *c, uint64_t event_token)
{
event_data_t *edata = _nc_table_find_64(&global.notify_state.event_table, event_token);
if (edata != NULL) {
NOTIFY_INTERNAL_CRASH(event_token, "Event token is already registered");
}
edata = calloc(1, sizeof(event_data_t));
edata->client = c;
edata->event_token = event_token;
_nc_table_insert_64(&global.notify_state.event_table, &edata->event_token);
global.notify_state.stat_portproc_alloc++;
}
static client_t *
cancel_xpc_event(uint64_t event_token)
{
event_data_t *edata = _nc_table_find_64(&global.notify_state.event_table, event_token);
if (edata == NULL) {
return NULL;
}
client_t *client = edata->client;
_nc_table_delete_64(&global.notify_state.event_table, event_token);
free(edata);
global.notify_state.stat_portproc_free++;
return client;
}
@ -351,7 +462,6 @@ server_preflight(audit_token_t audit, int token, uid_t *uid, gid_t *gid, pid_t *
*pid = xpid;
}
if (token > 0)
{
client_t *c;
uint64_t xcid = make_client_id(xpid, token);
@ -454,7 +564,7 @@ kern_return_t __notify_server_post_2
if (n == NULL)
{
*status = NOTIFY_STATUS_INVALID_NAME;
*status = NOTIFY_STATUS_NO_NID;
*name_id = UINT64_MAX;
call_statistics.post_no_op++;
}
@ -499,7 +609,7 @@ kern_return_t __notify_server_register_plain_2
)
{
client_t *c;
uint64_t nid, cid;
uint64_t nid, cid = 0;
uint32_t status;
uid_t uid = (uid_t)-1;
gid_t gid = (gid_t)-1;
@ -541,7 +651,7 @@ kern_return_t __notify_server_register_check_2
{
name_info_t *n;
uint32_t i, j, x, new_slot;
uint64_t cid;
uint64_t cid = 0;
client_t *c;
uid_t uid = (uid_t)-1;
gid_t gid = (gid_t)-1;
@ -644,7 +754,7 @@ kern_return_t __notify_server_register_signal_2
)
{
client_t *c;
uint64_t name_id, cid;
uint64_t name_id, cid = 0;
uint32_t status;
uid_t uid = (uid_t)-1;
gid_t gid = (gid_t)-1;
@ -684,7 +794,7 @@ kern_return_t __notify_server_register_file_descriptor_2
client_t *c;
int fd, flags;
uint32_t status;
uint64_t name_id, cid;
uint64_t name_id, cid = 0;
uid_t uid = (uid_t)-1;
gid_t gid = (gid_t)-1;
pid_t pid = (pid_t)-1;
@ -730,9 +840,8 @@ kern_return_t __notify_server_register_file_descriptor_2
return KERN_SUCCESS;
}
kern_return_t __notify_server_register_mach_port_2
static uint32_t _notify_register_mach_port_helper
(
mach_port_t server,
caddr_t name,
int token,
mach_port_t port,
@ -740,7 +849,7 @@ kern_return_t __notify_server_register_mach_port_2
)
{
client_t *c;
uint64_t name_id, cid;
uint64_t name_id, cid = 0;
uint32_t status;
uid_t uid = (uid_t)-1;
gid_t gid = (gid_t)-1;
@ -748,7 +857,7 @@ kern_return_t __notify_server_register_mach_port_2
if (port == MACH_PORT_DEAD || port == MACH_PORT_NULL)
{
return KERN_SUCCESS;
return NOTIFY_STATUS_INVALID_PORT_INTERNAL;
}
server_preflight(audit, token, &uid, &gid, &pid, &cid);
@ -762,15 +871,42 @@ kern_return_t __notify_server_register_mach_port_2
if (status != NOTIFY_STATUS_OK)
{
mach_port_deallocate(mach_task_self(), port);
return KERN_SUCCESS;
return NOTIFY_STATUS_NOT_AUTHORIZED;
}
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, audit);
register_proc(c, pid);
register_port(c, port);
bool success = register_port(c, port);
if (!success) {
port_proc_cancel_client(c);
}
// The mach_port_dealloc of port is done when the registration is cancelled
return NOTIFY_STATUS_OK;
}
kern_return_t __notify_server_register_mach_port_2
(
mach_port_t server,
caddr_t name,
int token,
mach_port_t port,
audit_token_t audit
)
{
notify_state_t *ns = &global.notify_state;
if (_nc_table_find_n(&ns->port_table, port) == NULL)
{
// Port was not created by notifyd
mach_port_deallocate(mach_task_self(), port);
return KERN_SUCCESS;
}
(void)_notify_register_mach_port_helper(name, token, port, audit);
return KERN_SUCCESS;
}
@ -1172,7 +1308,6 @@ kern_return_t __notify_server_regenerate
audit_token_t audit
)
{
kern_return_t kstatus;
pid_t pid = (pid_t)-1;
int size;
name_info_t *n;
@ -1206,16 +1341,16 @@ kern_return_t __notify_server_regenerate
case NOTIFY_TYPE_MEMORY:
{
/* prev_slot must be between 0 and global.nslots */
if ((prev_slot < 0) || (prev_slot >= global.nslots))
if ((uint32_t)prev_slot >= global.nslots)
{
*status = NOTIFY_STATUS_INVALID_REQUEST;
return KERN_SUCCESS;
}
kstatus = __notify_server_register_check_2(server, name, token, &size, new_slot, new_nid, status, audit);
(void)__notify_server_register_check_2(server, name, token, &size, (int *)new_slot, new_nid, status, audit);
if (*status == NOTIFY_STATUS_OK)
{
if ((*new_slot != UINT32_MAX) && (global.last_shm_base != NULL))
if (((uint32_t)*new_slot != SLOT_NONE) && (global.last_shm_base != NULL))
{
global.shared_memory_base[*new_slot] = global.shared_memory_base[*new_slot] + global.last_shm_base[prev_slot] - 1;
global.last_shm_base[prev_slot] = 0;
@ -1225,19 +1360,20 @@ kern_return_t __notify_server_regenerate
}
case NOTIFY_TYPE_PLAIN:
{
kstatus = __notify_server_register_plain_2(server, name, token, audit);
break;
}
case NOTIFY_TYPE_PORT:
{
kstatus = __notify_server_register_mach_port_2(server, name, token, port, audit);
(void)__notify_server_register_plain_2(server, name, token, audit);
break;
}
case NOTIFY_TYPE_SIGNAL:
{
kstatus = __notify_server_register_signal_2(server, name, token, sig, audit);
(void)__notify_server_register_signal_2(server, name, token, sig, audit);
break;
}
case NOTIFY_TYPE_COMMON_PORT:
{
(void)__notify_server_register_common_port(server, name, token, audit);
break;
}
case NOTIFY_TYPE_PORT:
case NOTIFY_TYPE_FILE: /* fall through */
default:
{
@ -1348,3 +1484,228 @@ kern_return_t __notify_server_dump
return KERN_SUCCESS;
}
static uid_t
xpc_event_token_get_uid(uint64_t event_token)
{
#if TARGET_OS_OSX
au_asid_t asid = xpc_event_publisher_get_subscriber_asid(global.notify_state.event_publisher, event_token);
auditinfo_addr_t info = { 0 };
info.ai_asid = asid;
int ret = auditon(A_GETSINFO_ADDR, &info, sizeof(info));
if (ret != 0) {
log_message(ASL_LEVEL_WARNING, "auditon on asid %d failed with errno %d, skipping registration\n", asid, errno);
return KAUTH_UID_NONE;
}
return info.ai_auid;
#else // TARGET_OS_OSX
// XPC event registrations historically bypassed UID permission checks on
// iOS since those were coming from UEA running as root. Preserve that and
// return root UID.
// There isn't a way to obtain the UID for an event token on iOS.
// rdar://problem/50776875
(void)event_token;
return 0; // root
#endif // TARGET_OS_OSX
}
void
notifyd_matching_register(uint64_t event_token, xpc_object_t descriptor)
{
assert(xpc_get_type(descriptor) == XPC_TYPE_DICTIONARY);
const char *name = xpc_dictionary_get_string(descriptor, NOTIFY_XPC_EVENT_PAYLOAD_KEY_NAME);
// Use bogus PID for XPC event registrations
pid_t pid = -1;
int token = global.next_no_client_token++;
call_statistics.reg++;
call_statistics.reg_xpc_event++;
log_message(ASL_LEVEL_DEBUG, "notifyd_matching_register %s %d %llu\n", name, token, event_token);
uid_t uid = xpc_event_token_get_uid(event_token);
if (uid == KAUTH_UID_NONE) {
return;
}
// notifyd can't call getpwuid_r to find out GID for UID as it deadlocks
// with opendirectoryd. Use bogus GID to fail group access checks if any.
gid_t gid = KAUTH_GID_NONE;
uint64_t unused_nid = 0;
uint32_t status = _notify_lib_register_xpc_event(&global.notify_state, name, pid, token, event_token, uid, gid, &unused_nid);
if (status != NOTIFY_STATUS_OK) {
if (status != NOTIFY_STATUS_NOT_AUTHORIZED) {
log_message(ASL_LEVEL_WARNING, "_notify_lib_register_xpc_event failed with status %u\n", status);
}
return;
}
client_t *c = _nc_table_find_64(&global.notify_state.client_table, make_client_id(pid, token));
if (c == NULL) {
NOTIFY_INTERNAL_CRASH(0, "Can't find client after registering an event");
}
// Don't register_proc since PID is bogus
register_xpc_event(c, event_token);
}
void
notifyd_matching_unregister(uint64_t event_token)
{
client_t *c = cancel_xpc_event(event_token);
if (c == NULL) {
return; // if registration was denied, there wouldn't be anything to unregister
}
_notify_lib_cancel_client(&global.notify_state, c);
}
kern_return_t __notify_generate_common_port
(
mach_port_t server,
uint32_t *status,
mach_port_t *out_port,
audit_token_t audit
)
{
mach_port_t port;
kern_return_t kstatus;
*status = NOTIFY_STATUS_OK;
*out_port = MACH_PORT_NULL;
pid_t pid = audit_token_to_pid(audit);
log_message(ASL_LEVEL_DEBUG, "__notify_generate_common_port %d\n", pid);
// Create a proc object if one doesn't exist
notify_state_t *ns = &global.notify_state;
proc_data_t *pdata = _nc_table_find_n(&ns->proc_table, pid);
if (!pdata) {
pdata = proc_create(ns, NULL, pid);
}
// It's possible the process may have already generated the common port,
// such as if the process calls exec.
if(pdata->common_port_data != NULL)
{
client_t *c, *tmp;
LIST_FOREACH_SAFE(c, &pdata->common_port_data->clients, client_pid_entry, tmp) {
port_proc_cancel_client(c);
}
common_port_free(pdata->common_port_data);
pdata->common_port_data = NULL;
}
mach_port_options_t opts = {
.flags = MPO_QLIMIT,
.mpl.mpl_qlimit = 16,
};
kstatus = mach_port_construct(mach_task_self(), &opts, 0, &port);
assert(kstatus == KERN_SUCCESS);
// This right will be released in do_mach_notify_dead_name/port_free
kstatus = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
assert(kstatus == KERN_SUCCESS);
pdata->common_port_data = common_port_create(&global.notify_state, port);
*out_port = port;
return KERN_SUCCESS;
}
kern_return_t __notify_server_register_common_port
(
mach_port_t server,
caddr_t name,
int token,
audit_token_t audit
)
{
client_t *c;
uint64_t name_id, cid = 0;
uint32_t status;
uid_t uid = (uid_t)-1;
gid_t gid = (gid_t)-1;
pid_t pid = (pid_t)-1;
server_preflight(audit, token, &uid, &gid, &pid, &cid);
call_statistics.reg++;
call_statistics.reg_common++;
log_message(ASL_LEVEL_DEBUG, "__notify_server_register_common_port %s %d %d\n", name, pid, token);
notify_state_t *ns = &global.notify_state;
proc_data_t *proc = _nc_table_find_n(&ns->proc_table, pid);
if (!proc || !proc->common_port_data) {
// Client doesn't have a common port set up
log_message(ASL_LEVEL_DEBUG, "_notify_server_register_common_port FAILED %s %d %d\n", name, pid, token);
return KERN_SUCCESS;
}
status = _notify_lib_register_common_port(ns, name, pid, token, uid, gid, &name_id);
if (status != NOTIFY_STATUS_OK)
{
return KERN_SUCCESS;
}
c = _nc_table_find_64(&ns->client_table, cid);
if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, audit);
proc_add_client(proc, c, pid);
LIST_INSERT_HEAD(&proc->common_port_data->clients, c, client_port_entry);
return KERN_SUCCESS;
}
kern_return_t __notify_server_register_mach_port_3
(
mach_port_t server,
caddr_t name,
int token,
uint32_t *status,
mach_port_t *out_port,
audit_token_t audit
)
{
mach_port_t port;
kern_return_t kstatus;
*status = NOTIFY_STATUS_OK;
*out_port = MACH_PORT_NULL;
mach_port_options_t opts = {
.flags = MPO_QLIMIT,
.mpl.mpl_qlimit = 16,
};
kstatus = mach_port_construct(mach_task_self(), &opts, 0, &port);
assert(kstatus == KERN_SUCCESS);
// This right will be released in do_mach_notify_dead_name/port_free
kstatus = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
assert(kstatus == KERN_SUCCESS);
*status = _notify_register_mach_port_helper(name, token, port, audit);
if (*status == NOTIFY_STATUS_OK)
{
*out_port = port;
}
else
{
mach_port_destruct(mach_task_self(), port, -1, 0);
}
return KERN_SUCCESS;
}

View File

@ -43,11 +43,11 @@
#include <TargetConditionals.h>
#include <bsm/libbsm.h>
#include <servers/bootstrap.h>
#include <os/trace_private.h>
#include "pathwatch.h"
#include "service.h"
#include "pathwatch.h"
#include "timer.h"
#include "notify_internal.h"
#include "notify.h"
@ -59,7 +59,7 @@
#define CRSetCrashLogMessage(msg) /**/
#define forever for(;;)
#define IndexNull -1
#define IndexNull (unsigned int)~0
/* Compile flags */
#define RUN_TIME_CHECKS
@ -75,12 +75,6 @@ static char *_debug_log_path;
#define DEBUG_LOG_PATH "/var/log/notifyd.log"
#endif
#define N_NOTIFY_TYPES 6
static int notifyd_token;
static char *status_file = NULL;
struct global_s global;
@ -111,12 +105,14 @@ notify_type_name(uint32_t t)
{
switch (t)
{
case NOTIFY_TYPE_NONE: return "none ";
case NOTIFY_TYPE_MEMORY: return "memory";
case NOTIFY_TYPE_PLAIN: return "plain ";
case NOTIFY_TYPE_PORT: return "port ";
case NOTIFY_TYPE_FILE: return "file ";
case NOTIFY_TYPE_SIGNAL: return "signal";
case NOTIFY_TYPE_NONE: return "none ";
case NOTIFY_TYPE_MEMORY: return "memory";
case NOTIFY_TYPE_PLAIN: return "plain ";
case NOTIFY_TYPE_PORT: return "port ";
case NOTIFY_TYPE_FILE: return "file ";
case NOTIFY_TYPE_SIGNAL: return "signal";
case NOTIFY_TYPE_XPC_EVENT: return "event ";
case NOTIFY_TYPE_COMMON_PORT: return "common";
default: return "unknown";
}
@ -136,18 +132,9 @@ fprint_client(FILE *f, client_t *c)
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->state_and_type & NOTIFY_TYPE_MASK));
switch(c->state_and_type & NOTIFY_TYPE_MASK)
fprintf(f, "type: %s\n", notify_type_name(notify_get_type(c->state_and_type)));
switch(notify_get_type(c->state_and_type))
{
case NOTIFY_TYPE_NONE:
break;
case NOTIFY_TYPE_PLAIN:
break;
case NOTIFY_TYPE_MEMORY:
break;
case NOTIFY_TYPE_PORT:
fprintf(f, "mach port: 0x%08x\n", c->deliver.port);
break;
@ -160,7 +147,19 @@ fprint_client(FILE *f, client_t *c)
fprintf(f, "signal: %d\n", c->deliver.sig);
break;
default: break;
case NOTIFY_TYPE_XPC_EVENT:
fprintf(f, "xpc event: %llu\n", c->deliver.event_token);
break;
case NOTIFY_TYPE_COMMON_PORT:
fprintf(f, "common port\n");
break;
case NOTIFY_TYPE_NONE:
case NOTIFY_TYPE_PLAIN:
case NOTIFY_TYPE_MEMORY:
default:
break;
}
}
@ -173,7 +172,7 @@ fprint_quick_client(FILE *f, client_t *c)
fprintf(f, "%llu,%d,%d,%u,%u,%u,", c->cid.hash_key, c->cid.pid, c->cid.token,
c->lastval, c->suspend_count, 0);
switch(c->state_and_type & NOTIFY_TYPE_MASK)
switch(notify_get_type(c->state_and_type))
{
case NOTIFY_TYPE_PORT:
fprintf(f, "port,0x%08x\n", c->deliver.port);
@ -191,6 +190,14 @@ fprint_quick_client(FILE *f, client_t *c)
fprintf(f, "check,0\n");
break;
case NOTIFY_TYPE_XPC_EVENT:
fprintf(f, "event,%llu\n", c->deliver.event_token);
break;
case NOTIFY_TYPE_COMMON_PORT:
fprintf(f, "common-port\n");
break;
case NOTIFY_TYPE_NONE:
case NOTIFY_TYPE_PLAIN:
default:
@ -215,7 +222,7 @@ fprint_quick_name_info(FILE *f, name_info_t *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)
if (n->slot == SLOT_NONE)
{
fprintf(f, "-1,");
}
@ -257,11 +264,11 @@ fprint_name_info(FILE *f, const char *name, name_info_t *n, pid_t *max_pid)
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-");
if (n->slot == SLOT_NONE) fprintf(f, "slot: -unassigned-");
else
{
fprintf(f, "slot: %u", n->slot);
if (global.shared_memory_refcount[n->slot] != -1)
if (global.shared_memory_refcount[n->slot] != SLOT_NONE)
fprintf(f, " = %u (%u)", global.shared_memory_base[n->slot], global.shared_memory_refcount[n->slot]);
}
fprintf(f, "\n");
@ -272,20 +279,22 @@ fprint_name_info(FILE *f, const char *name, name_info_t *n, pid_t *max_pid)
LIST_FOREACH(c, &n->subscriptions, client_subscription_entry)
{
if ((c->cid.pid != (pid_t)-1) && (c->cid.pid > *max_pid)) *max_pid = c->cid.pid;
if ((c->cid.pid != (uint32_t)~0) && (c->cid.pid > (uint32_t)*max_pid)) *max_pid = (pid_t)c->cid.pid;
switch (c->state_and_type & NOTIFY_TYPE_MASK)
switch (notify_get_type(c->state_and_type))
{
case NOTIFY_TYPE_MEMORY: reg[1]++; break;
case NOTIFY_TYPE_PLAIN: reg[2]++; break;
case NOTIFY_TYPE_PORT: reg[3]++; break;
case NOTIFY_TYPE_FILE: reg[4]++; break;
case NOTIFY_TYPE_SIGNAL: reg[5]++; break;
case NOTIFY_TYPE_XPC_EVENT: reg[6]++; break;
case NOTIFY_TYPE_COMMON_PORT: reg[7]++; break;
default: reg[0]++;
}
}
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]);
fprintf(f, "types: none %u memory %u plain %u port %u file %u signal %u event %u common %u\n", reg[0], reg[1], reg[2], reg[3], reg[4], reg[5], reg[6], reg[7]);
LIST_FOREACH(c, &n->subscriptions, client_subscription_entry)
{
@ -297,7 +306,7 @@ fprint_name_info(FILE *f, const char *name, name_info_t *n, pid_t *max_pid)
static void
fprint_quick_status(FILE *f)
{
int32_t i;
uint32_t i;
fprintf(f, "--- GLOBALS ---\n");
fprintf(f, "%u slots (current id %u)\n", global.nslots, global.slot_id);
@ -317,6 +326,8 @@ fprint_quick_status(FILE *f)
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, " event %llu\n", call_statistics.reg_xpc_event);
fprintf(f, " common %llu\n", call_statistics.reg_common);
fprintf(f, "\n");
fprintf(f, "check %llu\n", call_statistics.check);
fprintf(f, "cancel %llu\n", call_statistics.cancel);
@ -345,7 +356,6 @@ fprint_quick_status(FILE *f)
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];
@ -385,17 +395,17 @@ fprint_quick_status(FILE *f)
fprintf(f, "\n");
fprintf(f, "--- PUBLIC SERVICE ---\n");
_nc_table_foreach(&global.notify_state.name_table, ^bool(void *_n) {
name_info_t *n = _n;
_nc_table_foreach_64(&global.notify_state.client_table, ^bool(void *_c){
client_t *c = _c;
name_info_t *n = c->name_info;
svc_info_t *info;
path_node_t *node;
timer_t *timer;
if (n->private == NULL) return true;
info = (svc_info_t *)n->private;
info = service_info_get(c->service_index);
if (info == NULL) return true;
if (info->type == 0)
{
fprintf(f, "Null service: %s\n", n->name);
@ -405,28 +415,6 @@ fprint_quick_status(FILE *f)
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);
@ -443,7 +431,6 @@ fprint_quick_status(FILE *f)
svc_info_t *info;
name_info_t *n;
path_node_t *node;
timer_t *timer;
if (c->service_index == 0) return true;
@ -459,28 +446,6 @@ fprint_quick_status(FILE *f)
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;
});
@ -493,7 +458,7 @@ static void
fprint_status(FILE *f)
{
__block pid_t pid, max_pid;
int32_t i;
uint32_t i;
max_pid = 0;
@ -515,6 +480,8 @@ fprint_status(FILE *f)
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, " event %llu\n", call_statistics.reg_xpc_event);
fprintf(f, " common %llu\n", call_statistics.reg_common);
fprintf(f, "\n");
fprintf(f, "check %llu\n", call_statistics.check);
fprintf(f, "cancel %llu\n", call_statistics.cancel);
@ -543,7 +510,6 @@ fprint_status(FILE *f)
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);
{
@ -595,15 +561,15 @@ fprint_status(FILE *f)
fprintf(f, "--- PUBLIC SERVICE ---\n");
_nc_table_foreach(&global.notify_state.name_table, ^bool(void *_n) {
name_info_t *n = _n;
_nc_table_foreach_64(&global.notify_state.client_table, ^bool(void *_c){
client_t *c = _c;
name_info_t *n = c->name_info;
svc_info_t *info;
path_node_t *node;
timer_t *timer;
if (n->private == NULL) return true;
info = (svc_info_t *)n->private;
info = service_info_get(c->service_index);
if (info == NULL) return true;
if (info->type == 0)
{
@ -614,28 +580,6 @@ fprint_status(FILE *f)
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);
@ -653,7 +597,6 @@ fprint_status(FILE *f)
name_info_t *n;
svc_info_t *info;
path_node_t *node;
timer_t *timer;
if (c->service_index == 0) return true;
@ -669,28 +612,6 @@ fprint_status(FILE *f)
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;
});
@ -700,7 +621,7 @@ fprint_status(FILE *f)
fprintf(f, "--- PROCESSES ---\n");
for (pid = 0; pid <= max_pid; pid++)
{
int mem_count, plain_count, file_count, port_count, sig_count;
int mem_count, plain_count, file_count, port_count, sig_count, event_count, common_port_count;
proc_data_t *pdata;
client_t *c;
@ -712,9 +633,11 @@ fprint_status(FILE *f)
file_count = 0;
port_count = 0;
sig_count = 0;
event_count = 0;
common_port_count = 0;
LIST_FOREACH(c, &pdata->clients, client_pid_entry) {
switch(c->state_and_type & NOTIFY_TYPE_MASK) {
switch(notify_get_type(c->state_and_type)) {
case NOTIFY_TYPE_NONE:
break;
@ -738,6 +661,14 @@ fprint_status(FILE *f)
sig_count++;
break;
case NOTIFY_TYPE_XPC_EVENT:
event_count++;
break;
case NOTIFY_TYPE_COMMON_PORT:
common_port_count++;
break;
default:
break;
}
@ -745,10 +676,10 @@ fprint_status(FILE *f)
fprintf(f, "pid: %u ", pid);
fprintf(f, "memory %u plain %u port %u file %u signal %u\n",
mem_count, plain_count, port_count, file_count, sig_count);
fprintf(f, "memory %u plain %u port %u file %u signal %u event %u common %u\n",
mem_count, plain_count, port_count, file_count, sig_count, event_count, common_port_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, " %s: %s\n", notify_type_name(notify_get_type(c->state_and_type)), c->name_info->name);
}
fprintf(f, "\n");
@ -887,7 +818,7 @@ daemon_post_client(uint64_t cid)
c = _nc_table_find_64(&global.notify_state.client_table, cid);
if (c == NULL) return;
if ((c->state_and_type & NOTIFY_TYPE_MEMORY) && (c->name_info != NULL) && (c->name_info->slot != (uint32_t)-1))
if (notify_is_type(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]++;
}
@ -952,7 +883,7 @@ string_list_free(char **l)
static char **
string_insert(char *s, char **l, unsigned int x)
{
int i, len;
unsigned int i, len;
if (s == NULL) return l;
if (l == NULL)
@ -1078,7 +1009,7 @@ init_config(void)
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_register_plain(&global.notify_state, NOTIFY_IPC_VERSION_NAME, -1, global.next_no_client_token++, -1, 0, 0, &nid);
_notify_lib_set_state(&global.notify_state, nid, val64, 0, 0);
/* Check config file */
@ -1109,7 +1040,11 @@ init_config(void)
line[strlen(line) - 1] = '\0';
args = explode(line, "\t ");
argslen = string_list_length(args);
if (argslen == 0) continue;
if (argslen == 0)
{
string_list_free(args);
continue;
}
if (!strcasecmp(args[0], "monitor")) {
if (argslen < 3)
@ -1117,24 +1052,12 @@ init_config(void)
string_list_free(args);
continue;
}
_notify_lib_register_plain(&global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid);
_notify_lib_register_plain(&global.notify_state, args[1], -1, global.next_no_client_token++, -1, 0, 0, &nid);
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);
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)
{
@ -1142,7 +1065,7 @@ init_config(void)
continue;
}
_notify_lib_register_plain(&global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid);
_notify_lib_register_plain(&global.notify_state, args[1], -1, global.next_no_client_token++, -1, 0, 0, &nid);
if (argslen == 3)
{
val64 = atoll(args[2]);
@ -1172,6 +1095,8 @@ init_config(void)
} else if (!strcasecmp(args[0], "quit")) {
string_list_free(args);
break;
} else {
string_list_free(args);
}
}
@ -1290,7 +1215,8 @@ main(int argc, const char *argv[])
{
const char *service_name;
const char *shm_name;
uint32_t i, status;
int i;
uint32_t status;
struct rlimit rlim;
kern_return_t kr;
@ -1303,6 +1229,7 @@ main(int argc, const char *argv[])
shm_name = SHM_ID;
notify_set_options(NOTIFY_OPT_DISABLE);
os_trace_set_mode(OS_TRACE_MODE_DISABLE);
/* remove limit of number of file descriptors */
rlim.rlim_max = RLIM_INFINITY;
@ -1319,6 +1246,7 @@ main(int argc, const char *argv[])
global.nslots = getpagesize() / sizeof(uint32_t);
_notify_lib_notify_state_init(&global.notify_state, NOTIFY_STATE_ENABLE_RESEND);
global.next_no_client_token = 1;
global.log_cutoff = ASL_LEVEL_ERR;
global.log_path = strdup(DEBUG_LOG_PATH);
@ -1375,15 +1303,14 @@ main(int argc, const char *argv[])
mach_port_options_t opts = {
.flags = MPO_STRICT | MPO_CONTEXT_AS_GUARD,
};
kr = mach_port_construct(current_task(), &opts, &global, &global.mach_notify_port);
kr = mach_port_construct(current_task(), &opts, (mach_port_context_t)&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);
// dispatch_mach_connect happens in XPC_EVENT_PUBLISHER_ACTION_INITIAL_BARRIER
global.mach_channel = dispatch_mach_create_f("com.apple.notifyd.channel",
global.workloop, NULL, notifyd_mach_channel_handler);
@ -1393,7 +1320,33 @@ main(int argc, const char *argv[])
#else
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);
// dispatch_mach_connect happens in XPC_EVENT_PUBLISHER_ACTION_INITIAL_BARRIER
xpc_event_publisher_t publisher = xpc_event_publisher_create("com.apple.notifyd.matching", global.workloop);
global.notify_state.event_publisher = publisher;
xpc_event_publisher_set_handler(publisher,
^(xpc_event_publisher_action_t action, uint64_t event_token, xpc_object_t descriptor) {
switch (action) {
case XPC_EVENT_PUBLISHER_ACTION_ADD:
notifyd_matching_register(event_token, descriptor);
break;
case XPC_EVENT_PUBLISHER_ACTION_REMOVE:
notifyd_matching_unregister(event_token);
break;
case XPC_EVENT_PUBLISHER_ACTION_INITIAL_BARRIER:
dispatch_mach_connect(
global.mach_notifs_channel, global.mach_notify_port,
MACH_PORT_NULL, NULL);
dispatch_mach_connect(
global.mach_channel, global.server_port,
MACH_PORT_NULL, NULL);
break;
}
});
xpc_event_publisher_set_error_handler(publisher, ^(int error) {
NOTIFY_INTERNAL_CRASH(error, "Event publisher error");
});
xpc_event_publisher_activate(publisher);
/* Set up SIGUSR1 */
global.sig_usr1_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL,

View File

@ -58,8 +58,9 @@ struct global_s
uint32_t *shared_memory_base;
uint32_t *shared_memory_refcount;
uint32_t *last_shm_base;
uint32_t log_cutoff;
int log_cutoff;
uint32_t log_default;
uint32_t next_no_client_token;
uint16_t service_info_count;
char *log_path;
};
@ -79,6 +80,8 @@ struct call_statistics_s
uint64_t reg_signal;
uint64_t reg_file;
uint64_t reg_port;
uint64_t reg_xpc_event;
uint64_t reg_common;
uint64_t cancel;
uint64_t suspend;
uint64_t resume;
@ -96,7 +99,6 @@ struct call_statistics_s
uint64_t set_owner;
uint64_t set_access;
uint64_t monitor_file;
uint64_t service_timer;
uint64_t service_path;
uint64_t cleanup;
uint64_t regenerate;
@ -114,6 +116,9 @@ 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);
void notifyd_matching_register(uint64_t event_token, xpc_object_t descriptor);
void notifyd_matching_unregister(uint64_t event_token);
dispatch_queue_t get_notifyd_workloop(void);
#endif /* _NOTIFY_DAEMON_H_ */

View File

@ -173,7 +173,6 @@ _path_stat(const char *path, int link, uid_t uid, gid_t gid)
pthread_setugid_np(uid, gid);
/* stat the file */
stat_status = -1;
if (link != 0)
{
stat_status = lstat(path, &sb);
@ -352,7 +351,7 @@ _vnode_event(vnode_t *vnode)
if (vnode == NULL) return;
if ((vnode->src != NULL) && (dispatch_source_testcancel(vnode->src))) return;
ulf = dispatch_source_get_data(vnode->src);
ulf = vnode->src ? dispatch_source_get_data(vnode->src) : 0;
flags = (uint32_t)ulf;
memset(&sb, 0, sizeof(struct stat));
@ -746,7 +745,9 @@ _path_node_init(const char *path)
{
name = malloc(len + 1);
assert(name != NULL);
strncpy(name, start, len);
if (start != NULL) {
strncpy(name, start, len);
}
name[len] = '\0';
}
@ -847,7 +848,9 @@ _path_node_update(path_node_t *pnode, uint32_t flags, vnode_t *vnode)
{
/* suspend pnode->src, and fire it after PNODE_COALESCE_TIME */
pnode->flags |= PATH_SRC_SUSPENDED;
dispatch_suspend(pnode->src);
if (pnode->src) {
dispatch_suspend(pnode->src);
}
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, PNODE_COALESCE_TIME);
_path_node_retain(pnode);
@ -859,7 +862,9 @@ _path_node_update(path_node_t *pnode, uint32_t flags, vnode_t *vnode)
});
}
dispatch_source_merge_data(pnode->src, data);
if (pnode->src) {
dispatch_source_merge_data(pnode->src, data);
}
}
}
@ -935,8 +940,6 @@ path_node_create(const char *path, audit_token_t audit, bool is_notifyd, uint32_
_path_node_update(pnode, 0, NULL);
dispatch_retain(queue);
pnode->src = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_OR, 0, 0, queue);
pnode->flags = mask & PATH_NODE_ALL;
if (is_notifyd)

View File

@ -31,13 +31,10 @@
#include "notifyd.h"
#include "service.h"
#include "pathwatch.h"
#include "timer.h"
#include "notify_internal.h"
#define NOTIFY_PATH_SERVICE "path:"
#define NOTIFY_PATH_SERVICE_LEN 5
#define NOTIFY_TIMER_SERVICE "timer:"
#define NOTIFY_TIMER_SERVICE_LEN 6
/* Libinfo global */
extern uint32_t gL1CacheEnabled;
@ -50,7 +47,6 @@ service_type(const char *name)
len = SERVICE_PREFIX_LEN;
if (strncmp(name, SERVICE_PREFIX, len)) return SERVICE_TYPE_NONE;
else if (!strncmp(name + len, NOTIFY_PATH_SERVICE, NOTIFY_PATH_SERVICE_LEN)) return SERVICE_TYPE_PATH_PRIVATE;
else if (!strncmp(name + len, NOTIFY_TIMER_SERVICE, NOTIFY_TIMER_SERVICE_LEN)) return SERVICE_TYPE_TIMER_PRIVATE;
return SERVICE_TYPE_NONE;
}
@ -75,7 +71,6 @@ int
service_open_path(const char *name, const char *path, uid_t uid, gid_t gid)
{
name_info_t *n;
svc_info_t *info;
path_node_t *node;
call_statistics.service_path++;
@ -85,20 +80,6 @@ service_open_path(const char *name, const char *path, uid_t uid, gid_t gid)
n = _nc_table_find(&global.notify_state.name_table, name);
if (n == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (n->private != NULL)
{
/* a notify key may only have one service associated with it */
info = (svc_info_t *)n->private;
if (info->type != SERVICE_TYPE_PATH_PUBLIC) return NOTIFY_STATUS_INVALID_REQUEST;
/* the client must be asking for the same path that is being monitored */
node = (path_node_t *)info->private;
if (strcmp(path, node->path)) return NOTIFY_STATUS_INVALID_REQUEST;
/* the name is already getting notifications for this path */
return NOTIFY_STATUS_OK;
}
{
audit_token_t audit;
memset(&audit, 0, sizeof(audit_token_t));
@ -109,13 +90,6 @@ service_open_path(const char *name, const char *path, uid_t uid, gid_t gid)
node->contextp = strdup(name);
info = (svc_info_t *)calloc(1, sizeof(svc_info_t));
assert(info != NULL);
info->type = SERVICE_TYPE_PATH_PUBLIC;
info->private = node;
n->private = info;
dispatch_source_set_event_handler(node->src, ^{
daemon_post((const char *)node->contextp, uid, gid);
});
@ -233,261 +207,6 @@ service_open_path_private(const char *name, client_t *c, const char *path, audit
return NOTIFY_STATUS_OK;
}
/* format: [+]nnnn[s|m|h|d] */
static int
parse_single_arg(const char *arg, int relative_ok, time_t *t)
{
const char *p, *q;
time_t now, val;
if (arg == NULL) return -1;
p = arg;
now = 0;
if ((relative_ok != 0) && ((*p == '+') || (*p == '-')))
{
p++;
now = time(NULL);
}
if ((*p < '0') || (*p > '9')) return -1;
q = strchr(p, '.');
if (q != NULL) q--;
else q = arg + strlen(arg) - 1;
#ifdef __LP64__
val = (time_t)atoll(p);
#else
val = (time_t)atoi(p);
#endif
if ((*q >= '0') && (*q <= '9'))
{}
else if (*q == 's')
{}
else if (*q == 'm')
{
val *= 60;
}
else if (*q == 'h')
{
val *= 3600;
}
else if (*q == 'd')
{
val *= 86400;
}
else
{
return -1;
}
if (*arg == '-') *t = now - val;
else *t = now + val;
return 0;
}
static uint32_t
parse_timer_args(const char *args, time_t *s, time_t *f, time_t *e, int32_t *d)
{
char *p;
uint32_t t;
if (args == NULL) return TIME_EVENT_NONE;
/* first arg is start time */
if (parse_single_arg(args, 1, s) != 0) return TIME_EVENT_NONE;
t = TIME_EVENT_ONESHOT;
p = strchr(args, '.');
if (p != NULL)
{
/* second arg is frequency */
p++;
if (parse_single_arg(p, 0, f) != 0) return TIME_EVENT_NONE;
t = TIME_EVENT_CLOCK;
p = strchr(p, '.');
if (p != NULL)
{
/* third arg is end time */
p++;
if (parse_single_arg(args, 1, e) != 0) return TIME_EVENT_NONE;
p = strchr(p, '.');
if (p != NULL)
{
/* fourth arg is day number */
p++;
*d = atoi(p);
t = TIME_EVENT_CAL;
}
}
}
if (f == 0) t = TIME_EVENT_ONESHOT;
return t;
}
int
service_open_timer(const char *name, const char *args)
{
uint32_t t;
time_t s, f, e;
int32_t d;
name_info_t *n;
svc_info_t *info;
timer_t *timer;
call_statistics.service_timer++;
n = _nc_table_find(&global.notify_state.name_table, name);
if (n == NULL) return NOTIFY_STATUS_INVALID_NAME;
s = f = e = 0;
d = 0;
t = parse_timer_args(args, &s, &f, &e, &d);
if (t == TIME_EVENT_NONE) return NOTIFY_STATUS_INVALID_REQUEST;
if (n->private != NULL)
{
/* a notify key may only have one service associated with it */
info = (svc_info_t *)n->private;
if (info->type != SERVICE_TYPE_TIMER_PUBLIC) return NOTIFY_STATUS_INVALID_REQUEST;
/* the client must be asking for the same timer that is active */
timer = (timer_t *)info->private;
if ((timer->type != t) || (timer->start != s) || (timer->freq != f) || (timer->end != e) || (timer->day != d)) return NOTIFY_STATUS_INVALID_REQUEST;
/* the name is already getting notifications for this timer */
return NOTIFY_STATUS_OK;
}
switch (t)
{
case TIME_EVENT_ONESHOT:
{
timer = timer_oneshot(s, global.workloop);
break;
}
case TIME_EVENT_CLOCK:
{
timer = timer_clock(s, f, e, global.workloop);
break;
}
case TIME_EVENT_CAL:
{
timer = timer_calendar(s, f, e, d, global.workloop);
break;
}
default:
{
return NOTIFY_STATUS_INVALID_TIME_EVENT;
}
}
if (timer == NULL) return NOTIFY_STATUS_TIMER_FAILED;
timer->contextp = strdup(name);
info = (svc_info_t *)calloc(1, sizeof(svc_info_t));
assert(info != NULL);
info->type = SERVICE_TYPE_TIMER_PUBLIC;
info->private = timer;
n->private = info;
dispatch_source_set_event_handler(timer->src, ^{
daemon_post((const char *)timer->contextp, 0, 0);
});
dispatch_activate(timer->src);
return NOTIFY_STATUS_OK;
}
int
service_open_timer_private(const char *name, client_t *c, const char *args)
{
uint32_t t;
time_t s, f, e;
int32_t d;
name_info_t *n;
svc_info_t *info;
timer_t *timer;
call_statistics.service_timer++;
n = _nc_table_find(&global.notify_state.name_table, name);
if (n == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (c == NULL) return NOTIFY_STATUS_NULL_INPUT;
s = f = e = 0;
d = 0;
t = parse_timer_args(args, &s, &f, &e, &d);
if (t == TIME_EVENT_NONE) return NOTIFY_STATUS_INVALID_REQUEST;
if (c->service_index != 0)
{
/* a client may only have one service */
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 */
timer = (timer_t *)info->private;
if ((timer->type != t) || (timer->start != s) || (timer->freq != f) || (timer->end != e) || (timer->day != d)) return NOTIFY_STATUS_INVALID_REQUEST;
/* the client is already getting notifications for this timer */
return NOTIFY_STATUS_OK;
}
switch (t)
{
case TIME_EVENT_ONESHOT:
{
timer = timer_oneshot(s, global.workloop);
break;
}
case TIME_EVENT_CLOCK:
{
timer = timer_clock(s, f, e, global.workloop);
break;
}
case TIME_EVENT_CAL:
{
timer = timer_calendar(s, f, e, d, global.workloop);
break;
}
default:
{
return NOTIFY_STATUS_INVALID_TIME_EVENT;
}
}
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->service_index = service_info_add(info);
dispatch_source_set_event_handler(timer->src, ^{
daemon_post_client(timer->context64);
});
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, audit_token_t audit)
@ -519,12 +238,6 @@ service_open(const char *name, client_t *client, audit_token_t audit)
return service_open_path_private(name, client, p, audit, flags);
}
case SERVICE_TYPE_TIMER_PRIVATE:
{
p = strchr(name, ':');
if (p != NULL) p++;
return service_open_timer_private(name, client, p);
}
default:
{
return NOTIFY_STATUS_INVALID_REQUEST;
@ -549,12 +262,6 @@ service_close(uint16_t service_index)
path_node_close((path_node_t *)info->private);
break;
}
case SERVICE_TYPE_TIMER_PUBLIC:
case SERVICE_TYPE_TIMER_PRIVATE:
{
timer_close((timer_t *)info->private);
break;
}
default:
{
}

View File

@ -27,8 +27,6 @@
#define SERVICE_TYPE_NONE 0
#define SERVICE_TYPE_PATH_PUBLIC 1
#define SERVICE_TYPE_PATH_PRIVATE 2
#define SERVICE_TYPE_TIMER_PUBLIC 3
#define SERVICE_TYPE_TIMER_PRIVATE 4
#define SERVICE_PREFIX "com.apple.system.notify.service."
#define SERVICE_PREFIX_LEN 32
@ -42,8 +40,6 @@ typedef struct
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, 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(uint16_t service_index);
void *service_info_get(uint16_t index);

View File

@ -1,409 +0,0 @@
/*
* Copyright (c) 2009-2010 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@
*/
#include <stdlib.h>
#include <string.h>
#include <Block.h>
#include "timer.h"
#define MINUTE 60
#define HOUR 3600
#define DAY 86400
static const uint8_t mlen[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/*
* Timed events
*
* Supported event types:
*
* Oneshot
* Every n seconds/minutes/hours/days/weeks
* Specific day of the month, every n months
* Specific weekday following specific day of the month, every n months
*
*/
static time_t
timer_next(timer_t *t, time_t now)
{
uint32_t y, m;
int32_t d, x, a, b, dd, wd;
struct tm tmp;
time_t next, tt, tod;
if (t == NULL) return 0;
switch (t->type)
{
case TIME_EVENT_ONESHOT:
{
/*
* oneshot time event
*/
if (t->start < now) return 0;
return t->start;
}
case TIME_EVENT_CLOCK:
{
/*
* event recurs every t->freq seconds
*/
/* t->end is the cut-off. If it's in the past, return 0 */
if ((t->end != 0) && (t->end < now)) return 0;
/* If the start time is in the future, that's the next occurrence */
if (t->start >= now) return t->start;
/* shouldn't happen, as TIME_EVENT_CLOCK should always recur */
if (t->freq == 0) return 0;
x = (int32_t)(((t->freq - 1) + now - t->start) / t->freq);
next = t->start + (x * t->freq);
return next;
}
case TIME_EVENT_CAL:
{
/*
* event recurs every t->freq months
* t->base gives us the starting month and year, and the time of day
* t->day specifies the day of the month (negative means relative to last day of the month)
* t->day is > 100 or < 100, then it means a weekday
* 101 = first monday, 102 = first tuesday, ..., 108 = second monday, and so on
* -101 = last monday, -102 = last tuesday, ..., -108 = second last monday, and so on
*/
/* t->end is the cut-off. If it's in the past, return 0 */
if ((t->end != 0) && (t->end < now)) return 0;
/* If the start time is in the future, that's the next occurrence */
if (t->start >= now) return t->start;
next = t->start;
/* If t->next is set from the last time we ran, and it is in the past, we can start there. */
if ((t->next > 0) && (t->next < now)) next = t->next;
while (next < now)
{
/* determine year, month, and time-of-day (clock time) of the next occurance */
memset(&tmp, 0, sizeof(struct tm));
localtime_r((const time_t *)&(next), &tmp);
y = tmp.tm_year;
m = tmp.tm_mon;
tod = tmp.tm_sec + (MINUTE * tmp.tm_min) + (HOUR * tmp.tm_hour);
m += t->freq;
if (m > 11)
{
y += (m / 12);
m %= 12;
}
/* we now have a year (y), a month (m), and a time of day (tod) */
if (t->day > 0)
{
if (t->day < 100)
{
/* easy case: day is the date of the month */
memset(&tmp, 0, sizeof(struct tm));
tmp.tm_year = y;
tmp.tm_mon = m;
tmp.tm_mday = t->day;
tmp.tm_isdst = -1;
next = mktime(&tmp) + tod;
continue;
}
else
{
/* t->day is a weekday */
wd = t->day - 100;
/* start by finding out the weekday of the first of the month */
memset(&tmp, 0, sizeof(struct tm));
tmp.tm_year = y;
tmp.tm_mon = m;
tmp.tm_mday = 1;
tmp.tm_isdst = -1;
tt = mktime(&tmp);
localtime_r((const time_t *)&tt, &tmp);
if (tmp.tm_wday == 0) tmp.tm_wday = 7;
x = 0;
if (tmp.tm_wday > (wd % 7)) x = (wd + 7) - tmp.tm_wday;
else x = wd - tmp.tm_wday;
tmp.tm_mday += x;
tmp.tm_isdst = -1;
next = mktime(&tmp) + tod;
continue;
}
}
if (t->day > -100)
{
/* nth day from the end of the month */
if (m == 1)
{
/* determine weekday of last day of February (== March 0) */
memset(&tmp, 0, sizeof(struct tm));
tmp.tm_year = y;
tmp.tm_mon = 2;
tmp.tm_mday = 0;
tmp.tm_isdst = -1;
tt = mktime(&tmp);
memset(&tmp, 0, sizeof(struct tm));
localtime_r((const time_t *)&(tt), &tmp);
d = tmp.tm_mday + t->day;
}
else
{
d = mlen[m] + t->day;
}
memset(&tmp, 0, sizeof(struct tm));
tmp.tm_year = y;
tmp.tm_mon = m;
tmp.tm_mday = d;
tmp.tm_isdst = -1;
next = mktime(&tmp) + tod;
continue;
}
/* t->day is a weekday relative to the end of the month */
if (m == 1)
{
/* determine weekday of last day of February (== March 0) */
memset(&tmp, 0, sizeof(struct tm));
tmp.tm_year = y;
tmp.tm_mon = 2;
tmp.tm_mday = 0;
tmp.tm_isdst = -1;
tt = mktime(&tmp);
memset(&tmp, 0, sizeof(struct tm));
localtime_r((const time_t *)&(tt), &tmp);
d = tmp.tm_mday;
}
else
{
d = mlen[m];
}
memset(&tmp, 0, sizeof(struct tm));
tmp.tm_year = y;
tmp.tm_mon = m;
tmp.tm_mday = d;
tmp.tm_isdst = -1;
dd = -1 * (t->day + 100);
a = dd % 7;
b = (dd + 6) / 7;
if (a <= tmp.tm_wday) b--;
tmp.tm_mday = ((a - tmp.tm_wday) + d) - (b * 7);
next = mktime(&tmp) + tod;
}
t->next = next;
return next;
}
default:
{
return 0;
}
}
}
/*
* This does the actual free.
* It is dispatched on the timer's dispatch source queue to make it safe.
*/
static void
timer_free(timer_t *t)
{
if (t == NULL) return;
if (t->contextp != NULL) free(t->contextp);
dispatch_release(t->t_src);
dispatch_release(t->t_queue);
memset(t, 0, sizeof(timer_t));
free(t);
}
void
timer_close(timer_t *t)
{
if (t == NULL) return;
if (t->t_src != NULL) dispatch_source_cancel(t->t_src);
/*
* We need to make sure that the source's event handler isn't currently running
* before we free the timer. We let the source's queue do the actual free.
*/
dispatch_async(t->t_queue, ^{ timer_free(t); });
}
timer_t *
timer_oneshot(time_t when, dispatch_queue_t queue)
{
timer_t *t;
time_t now;
dispatch_time_t trigger;
/* refuse a trigger time in the past */
now = time(0);
if (when <= now) return NULL;
t = calloc(1, sizeof(timer_t));
if (t == NULL) return NULL;
dispatch_retain(queue);
t->type = TIME_EVENT_ONESHOT;
t->start = when;
t->t_queue = queue;
t->t_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
t->src = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
trigger = dispatch_walltime(NULL, (t->start - now) * NSEC_PER_SEC);
dispatch_source_set_timer(t->t_src, trigger, NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(t->t_src, ^{
dispatch_source_merge_data(t->src, 1);
dispatch_source_cancel(t->t_src);
});
dispatch_activate(t->t_src);
return t;
}
timer_t *
timer_clock(time_t first, time_t freq_sec, time_t end, dispatch_queue_t queue)
{
timer_t *t;
time_t now;
dispatch_time_t trigger;
int64_t x;
if (freq_sec == 0) return timer_oneshot(first, queue);
now = time(0);
t = calloc(1, sizeof(timer_t));
if (t == NULL) return NULL;
t->type = TIME_EVENT_CLOCK;
if (first < now)
{
x = ((freq_sec - 1) + now - first) / freq_sec;
t->start = first + (x * freq_sec);
}
else
{
t->start = first;
}
t->end = end;
t->freq = (uint32_t)freq_sec;
t->t_queue = queue;
t->t_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
t->src = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
trigger = dispatch_walltime(NULL, (t->start - now) * NSEC_PER_SEC);
dispatch_source_set_timer(t->t_src, trigger, freq_sec * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(t->t_src, ^{
unsigned long n = dispatch_source_get_data(t->t_src);
dispatch_source_merge_data(t->src, n);
/* deactivate if this is the last time we want to trigger the client source */
if ((t->end > 0) && (t->end < (time(0) + freq_sec)))
{
dispatch_source_cancel(t->t_src);
}
});
dispatch_activate(t->t_src);
return t;
}
timer_t *
timer_calendar(time_t first, time_t freq_mth, time_t end, int day, dispatch_queue_t queue)
{
timer_t *t;
time_t next, now;
dispatch_time_t trigger;
if (freq_mth == 0) return timer_oneshot(first, queue);
now = time(0);
t = calloc(1, sizeof(timer_t));
if (t == NULL) return NULL;
t->type = TIME_EVENT_CAL;
t->start = first;
t->day = day;
t->end = end;
t->freq = (uint32_t)freq_mth;
t->t_queue = queue;
t->t_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
t->src = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
next = timer_next(t, now);
trigger = dispatch_walltime(NULL, (next - now) * NSEC_PER_SEC);
dispatch_source_set_timer(t->t_src, trigger, NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(t->t_src, ^{
unsigned long n = dispatch_source_get_data(t->t_src);
dispatch_source_merge_data(t->src, n);
time_t now = time(0);
time_t x = timer_next(t, now);
/* deactivate when there is no next time */
if (x == 0)
{
dispatch_source_cancel(t->t_src);
}
else
{
dispatch_source_set_timer(t->t_src, dispatch_walltime(NULL, (x - now) * NSEC_PER_SEC), NSEC_PER_SEC, 0);
}
});
dispatch_activate(t->t_src);
return t;
}

View File

@ -1,56 +0,0 @@
/*
* Copyright (c) 2009-2010 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@
*/
#include <time.h>
#include <stdint.h>
#include <dispatch/dispatch.h>
#define TIME_EVENT_NONE 0
#define TIME_EVENT_ONESHOT 1
#define TIME_EVENT_CLOCK 2
#define TIME_EVENT_CAL 3
/*
* Timer Event
*/
typedef struct
{
dispatch_source_t src;
dispatch_source_t t_src;
dispatch_queue_t t_queue;
void *contextp;
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);
void timer_close(timer_t *t);

0
notifyd/xcodescripts/mk_notify_conf.sh Normal file → Executable file
View File

View File

@ -209,7 +209,8 @@ process_event(int tid)
{
struct timeval now;
char tstr[32];
int status, index, needspace;
int status, needspace;
uint32_t index;
uint64_t state;
gettimeofday(&now, NULL);
@ -255,7 +256,6 @@ process_event(int tid)
{
if (needspace) printf(" ");
printf("%s", typename[reg[index].type]);
needspace = 1;
}
if (printopt != PRINT_QUIET) printf("\n");
@ -263,7 +263,7 @@ process_event(int tid)
if ((reg[index].count != IndexNull) && (reg[index].count != 0)) reg[index].count--;
if (reg[index].count == 0)
{
status = notify_cancel(tid);
(void)notify_cancel(tid);
reg_delete(index);
}
fflush(stdout);
@ -406,7 +406,7 @@ do_register(const char *name, uint32_t type, uint32_t signum, uint32_t count)
status = notify_register_signal(name, signum, &tid);
if (status != NOTIFY_STATUS_OK) return status;
status = notify_check(tid, &check);
(void)notify_check(tid, &check);
if (sig_src[signum] == NULL)
{
@ -432,7 +432,7 @@ do_register(const char *name, uint32_t type, uint32_t signum, uint32_t count)
status = notify_register_check(name, &tid);
if (status != NOTIFY_STATUS_OK) return status;
status = notify_check(tid, &check);
(void)notify_check(tid, &check);
if (timer_src == NULL)
{
@ -452,7 +452,7 @@ do_register(const char *name, uint32_t type, uint32_t signum, uint32_t count)
status = notify_register_plain(name, &tid);
if (status != NOTIFY_STATUS_OK) return status;
status = notify_check(tid, &check);
(void)notify_check(tid, &check);
if (timer_src == NULL)
{
@ -478,7 +478,8 @@ int
main(int argc, const char *argv[])
{
const char *name;
uint32_t i, n, signum, ntype, status, opts, nap;
int i;
uint32_t n, signum, ntype, status, opts, nap;
int tid;
uint64_t state;
@ -639,7 +640,7 @@ main(int argc, const char *argv[])
}
}
if (opts != 0) notify_set_options(opts);
if (opts != 0) notify_set_options(opts);
for (i = 1; i < argc; i++)
{

View File

@ -26,6 +26,7 @@
#include <os/base.h>
#include <stdint.h>
#include <stdbool.h>
#define _nc_table(key_t, _ns) \
struct _nc_table##_ns { \
@ -59,9 +60,11 @@ extern void _nc_table_delete(table_t *t, const char *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_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 *));
typedef bool (^payload_handler_t) (void *);
extern void _nc_table_foreach(table_t *t, OS_NOESCAPE payload_handler_t handler);
extern void _nc_table_foreach_n(table_n_t *t, OS_NOESCAPE payload_handler_t handler);
extern void _nc_table_foreach_64(table_64_t *t,OS_NOESCAPE payload_handler_t handler);
__END_DECLS

View File

@ -51,7 +51,7 @@ ns(_rehash)(struct ns() *t, int direction)
if (direction > 0) {
t->size += (1 << t->grow_shift);
if (t->size == (8 << t->grow_shift)) {
if (t->size == ((uint32_t)8 << t->grow_shift)) {
t->grow_shift++;
}
} else if (direction < 0) {

28
tests/Makefile Normal file
View File

@ -0,0 +1,28 @@
.PHONY: all installhdrs install $(DEVELOPER_DIR)/AppleInternal/Makefiles/darwintest/Makefile.targets $(DEVELOPER_DIR)/AppleInternal/Makefiles/darwintest/Makefile.common Makefile
PROJECT := Libnotify
TEST_DIR := tests/
DEVELOPER_DIR ?= /Applications/Xcode.app/Contents/Developer/
ifneq ($(wildcard $(DEVELOPER_DIR)/AppleInternal/Makefiles/darwintest/Makefile.common),)
include $(DEVELOPER_DIR)/AppleInternal/Makefiles/darwintest/Makefile.common
EXCLUDED_SOURCES := \
random_test.c \
notify_control.c \
notify_test_helper.c \
OTHER_LDFLAGS := -ldarwintest
include $(DEVELOPER_DIR)/AppleInternal/Makefiles/darwintest/Makefile.targets
# fall back if we can't find the Makefile to avoid an error in that case
else
all:
installhdrs:
install:
mkdir -p $(DSTROOT)/AppleInternal/CoreOS/tests/$(PROJECT)
endif

View File

@ -0,0 +1,63 @@
#include <stdlib.h>
#include <notify.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <darwintest.h>
#define KEY "com.apple.notify.test.dispatch_cancel_in_block"
#define COUNT 10
T_DECL(dispatch_cancel_in_block,
"Tests notify_cancel in dispatch block.",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
int i;
int status;
int tokens[COUNT];
T_LOG("Creating Notify queue");
dispatch_queue_t queue = dispatch_queue_create("Notify", NULL);
T_LOG("Creating dispatch group");
dispatch_group_t group = dispatch_group_create();
for (i = 0; i < COUNT; ++i)
{
dispatch_group_enter(group);
T_LOG("Registering dispatch notification listeners");
status = notify_register_dispatch(KEY, &tokens[i], queue, ^(int x){
int is_valid;
int status;
T_LOG("Received dispatch notification: %d", x);
status = notify_cancel(x);
T_EXPECT_EQ(status, NOTIFY_STATUS_OK,
"notify_cancel %d == NOTIFY_STATUS_OK", status);
is_valid = notify_is_valid_token(x);
T_EXPECT_EQ(is_valid, 0,
"notify_is_valid_token(%d) == %d", x, is_valid);
dispatch_group_leave(group);
});
T_EXPECT_EQ(status, NOTIFY_STATUS_OK,
"notify_register_dispatch %d == NOTIFY_STATUS_OK", status);
T_EXPECT_TRUE(tokens[i] >= 0, "token %d >= 0", status);
}
T_LOG("Posting notifications");
notify_post(KEY);
dispatch_group_notify(group, queue, ^(void) {
printf("Notified...\n");
T_END;
});
dispatch_release(queue);
dispatch_main();
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>RunAtLoad</key>
<true/>
<key>LaunchEvents</key>
<dict>
<key>com.apple.notifyd.matching</key>
<dict>
<key>foo</key>
<dict>
<key>Notification</key>
<string>com.apple.notifyd.test.matching</string>
</dict>
</dict>
</dict>
</dict>
</plist>

360
tests/notify_benchmark.c Normal file
View File

@ -0,0 +1,360 @@
//
// notify_benchmark.c
// darwintests
//
// Created by Brycen Wershing on 7/26/18.
//
#include <darwintest.h>
#include <notify.h>
#include <dispatch/dispatch.h>
#include "notify_private.h"
#include <stdlib.h>
static const uint32_t CNT = 10;
#if TARGET_OS_WATCH
static const uint32_t SPL = 1000;
#else
static const uint32_t SPL = 10000;
#endif
static const uint32_t CHECK_WEIGHT = 100;
static const uint32_t COAL_WEIGHT = 25;
// We use bench assert here as it has a lower observer effect than T_* checks.
#define bench_assert(X) if(!(X)) {T_FAIL("bench_assertion failed on line %d: %s", __LINE__, #X);}
static void
notify_fence(void)
{
int fence_token;
notify_register_check("com.apple.notify.test", &fence_token);
notify_cancel(fence_token);
}
T_DECL(notify_benchmark_post,
"notify benchmark post",
T_META_EASYPERF(true),
T_META_EASYPERF_ARGS("-p notifyd"),
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
uint32_t r;
uint32_t i, j;
int t[CNT];
char *n[CNT];
size_t l[CNT];
for (i = 0; i < CNT; i++)
{
r = asprintf(&n[i], "dummy.test.%d", i);
assert(r != -1);
l[i] = strlen(n[i]);
}
for (j = 0 ; j < SPL; j++)
{
/* Register Plain */
for (i = 0; i < CNT; i++)
{
r = notify_register_plain(n[i], &t[i]);
bench_assert(r == 0);
}
/* Post 1 */
for (i = 0; i < CNT; i++)
{
r = notify_post(n[i]);
bench_assert(r == 0);
}
/* Post 2 */
for (i = 0; i < CNT; i++)
{
r = notify_post(n[i]);
bench_assert(r == 0);
}
/* Post 3 */
for (i = 0; i < CNT; i++)
{
r = notify_post(n[i]);
bench_assert(r == 0);
}
/* Cancel Plain */
for (i = 0; i < CNT; i++)
{
r = notify_cancel(t[i]);
bench_assert(r == 0);
}
}
for (i = 0; i < CNT; i++)
{
free(n[i]);
}
T_PASS("Notify Benchmark Succeeded!");
}
T_DECL(notify_benchmark_state,
"notify benchmark state",
T_META_EASYPERF(true),
T_META_EASYPERF_ARGS("-p notifyd"),
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
uint32_t r;
unsigned i, j, k;
int t[CNT];
mach_port_t p[CNT];
char *n[CNT];
size_t l[CNT];
uint64_t s;
for (i = 0; i < CNT; i++)
{
r = asprintf(&n[i], "dummy.test.%d", i);
assert(r != -1);
l[i] = strlen(n[i]);
}
for (j = 0 ; j < SPL; j++)
{
/* Register Mach Port */
for (i = 0; i < CNT; i++)
{
r = notify_register_mach_port(n[i], &p[i], 0, &t[i]);
bench_assert(r == 0);
}
/* Set State 1 */
for (i = 0; i < CNT; i++)
{
r = notify_set_state(t[i], 1);
bench_assert(r == 0);
}
/* Get State */
for (i = 0; i < CNT; i++)
{
uint64_t dummy;
r = notify_get_state(t[i], &dummy);
bench_assert(r == 0);
}
/* Set State 2 */
for (i = 0; i < CNT; i++)
{
r = notify_set_state(t[i], 2);
bench_assert(r == 0);
}
/* Cancel Port */
for (i = 0; i < CNT; i++)
{
r = notify_cancel(t[i]);
bench_assert(r == 0);
}
}
for (i = 0; i < CNT; i++)
{
free(n[i]);
}
T_PASS("Notify Benchmark Succeeded!");
}
T_DECL(notify_benchmark_check,
"notify benchmark check",
T_META_EASYPERF(true),
T_META_EASYPERF_ARGS("-p notifyd"),
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
uint32_t r;
unsigned i, j, k;
int t[CNT];
char *n[CNT];
size_t l[CNT];
int check;
for (i = 0; i < CNT; i++)
{
r = asprintf(&n[i], "dummy.test.%d", i);
assert(r != -1);
l[i] = strlen(n[i]);
}
for (j = 0 ; j < SPL; j++)
{
/* Register Check */
for (i = 0; i < CNT; i++)
{
r = notify_register_check("com.apple.notify.test.check", &t[i]);
bench_assert(r == 0);
}
/* Check 1 */
for (i = 0; i < CNT; i++)
{
r = notify_check(t[i], &check);
bench_assert(r == 0);
bench_assert(check == 1);
}
/* Check 2 */
for (i = 0; i < CNT; i++)
{
for(k = 0; k < CHECK_WEIGHT; k++){
r = notify_check(t[i], &check);
bench_assert(r == 0);
bench_assert(check == 0);
}
}
/* Check 3 */
for (i = 0; i < CNT; i++)
{
for(k = 0; k < CHECK_WEIGHT; k++){
r = notify_check(t[i], &check);
bench_assert(r == 0);
bench_assert(check == 0);
}
}
notify_post("com.apple.notify.test.check");
notify_fence();
/* Check 4 */
for (i = 0; i < CNT; i++)
{
r = notify_check(t[i], &check);
bench_assert(r == 0);
bench_assert(check == 1);
}
/* Check 5 */
for (i = 0; i < CNT; i++)
{
for(k = 0; k < CHECK_WEIGHT; k++){
r = notify_check(t[i], &check);
bench_assert(r == 0);
bench_assert(check == 0);
}
}
/* Cancel Check */
for (i = 0; i < CNT; i++)
{
r = notify_cancel(t[i]);
bench_assert(r == 0);
}
}
for (i = 0; i < CNT; i++)
{
free(n[i]);
}
T_PASS("Notify Benchmark Succeeded!");
}
T_DECL(notify_benchmark_dispatch,
"notify benchmark dispatch",
T_META_EASYPERF(true),
T_META_EASYPERF_ARGS("-p notifyd"),
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
uint32_t r;
unsigned i, j, k;
int t[CNT];
int t_2[CNT];
char *n[CNT];
size_t l[CNT];
uint64_t s;
__block volatile int dispatch_changer;
dispatch_queue_t disp_q = dispatch_queue_create("Notify.Test", NULL);
for (i = 0; i < CNT; i++)
{
r = asprintf(&n[i], "dummy.test.%d", i);
assert(r != -1);
l[i] = strlen(n[i]);
}
for (j = 0 ; j < SPL; j++)
{
/* Register Dispatch 1 */
for (i = 0; i < CNT; i++)
{
r = notify_register_dispatch(n[i], &t[i], disp_q, ^(int x){
dispatch_changer = x;
});
bench_assert(r == 0);
}
for (k = 0; k < COAL_WEIGHT; k++){
/* Register Dispatch 2 (Coalesced) */
for (i = 0; i < CNT; i++)
{
r = notify_register_dispatch(n[i], &t_2[i], disp_q, ^(int x){
dispatch_changer = x;
});
bench_assert(r == 0);
}
/* Cancel Coalesced Dispatch */
for (i = 0; i < CNT; i++){
r = notify_cancel(t_2[i]);
bench_assert(r == 0);
}
}
/* Cancel Dispatch */
for (i = 0; i < CNT; i++)
{
r = notify_cancel(t[i]);
bench_assert(r == 0);
}
}
for (i = 0; i < CNT; i++)
{
free(n[i]);
}
T_PASS("Notify Benchmark Succeeded!");
}

32
tests/notify_control.c Normal file
View File

@ -0,0 +1,32 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <notify_private.h>
int main(int argc, char *argv[])
{
pid_t pid;
int i;
uint32_t status;
for (i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "-s"))
{
pid = atoi(argv[++i]);
status = notify_suspend_pid(pid);
if (status != 0) printf("suspend pid %d failed status %u\n", pid, status);
else printf("suspend pid %d OK\n", pid);
}
else if (!strcmp(argv[i], "-r"))
{
pid = atoi(argv[++i]);
status = notify_resume_pid(pid);
if (status != 0) printf("resume pid %d failed status %u\n", pid, status);
else printf("resume pid %d OK\n", pid);
}
}
return 0;
}

View File

@ -0,0 +1,61 @@
#include <stdlib.h>
#include <stdio.h>
#include <notify.h>
#include <notify_private.h>
#include <darwintest.h>
#define KEY1 "com.apple.notify.test.disable"
#define KEY2 "com.apple.notify.test.disable.fail"
T_DECL(notify_disable_test,
"notify disable test",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
int token1, token2, status, fd;
uint64_t state;
token1 = NOTIFY_TOKEN_INVALID;
token2 = NOTIFY_TOKEN_INVALID;
fd = -1;
status = notify_register_file_descriptor(KEY1, &fd, 0, &token1);
T_EXPECT_EQ(status, NOTIFY_STATUS_OK, "notify_register_file_descriptor %d == NOTIFY_STATUS_OK", status);
state = 123454321;
status = notify_set_state(token1, state);
T_EXPECT_EQ(status, NOTIFY_STATUS_OK, "notify_set_state %d == NOTIFY_STATUS_OK", status);
state = 0;
status = notify_get_state(token1, &state);
T_EXPECT_EQ(status, NOTIFY_STATUS_OK, "notify_get_state %d == NOTIFY_STATUS_OK", status);
T_EXPECT_EQ(state, 123454321ULL, "notify_get_state %llu == 123454321", state);
// Disable
T_LOG("notify_set_options(NOTIFY_OPT_DISABLE)");
notify_set_options(NOTIFY_OPT_DISABLE);
status = notify_register_check(KEY2, &token2);
T_EXPECT_NE(status, NOTIFY_STATUS_OK, "notify_register_check %d != NOTIFY_STATUS_OK", status);
state = 0;
status = notify_get_state(token1, &state);
T_EXPECT_NE(status, NOTIFY_STATUS_OK, "notify_get_state %d != NOTIFY_STATUS_OK", status);
// Re-enable
T_LOG("notify_set_options(NOTIFY_OPT_ENABLE)");
notify_set_options(NOTIFY_OPT_ENABLE);
state = 0;
status = notify_get_state(token1, &state);
T_EXPECT_EQ(status, NOTIFY_STATUS_OK, "notify_get_state %d == NOTIFY_STATUS_OK", status);
T_EXPECT_EQ(state, 123454321ULL, "notify_get_state %llu == 123454321", state);
T_LOG("checking token validity");
status = notify_is_valid_token(token1);
T_EXPECT_GT(status, 0, "notify_is_valid_token(token1) > 0 (%d)", status);
T_LOG("canceling token1");
notify_cancel(token1);
status = notify_is_valid_token(token1);
T_EXPECT_EQ(status, 0, "notify_is_valid_token(token1) == 0 (%d)", status);
}

57
tests/notify_exec.c Normal file
View File

@ -0,0 +1,57 @@
//
// notify_exec.c
// darwintests
//
// Created by Brycen Wershing on 2/10/20.
//
#include <darwintest.h>
#include <notify.h>
#include <notify_private.h>
#include <dispatch/dispatch.h>
#include <stdlib.h>
#include <unistd.h>
extern char **environ;
T_DECL(notify_exec,
"notify exec",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "true"))
{
pid_t pid = fork();
if (pid == 0) {
printf("Child started up\n");
char *argv[3];
argv[0] = "notify_test_helper";
argv[1] = "Continue";
argv[2] = NULL;
execve("/AppleInternal/Tests/Libnotify/notify_test_helper" ,argv , environ);
printf("execve failed with %d\n", errno);
abort();
} else {
int status;
T_LOG("Fork returned %d", pid);
pid = waitpid(pid, &status, 0);
if (pid == -1) {
T_FAIL("wait4 failed with %d", errno);
return;
}
if (!WIFEXITED(status)) {
T_FAIL("Unexpected helper termination");
return;
}
int exitStatus = WEXITSTATUS(status);
if (exitStatus == 42) {
T_PASS("Succeeded!");
} else {
T_FAIL("Failed with %d", exitStatus);
}
}
}

6
tests/notify_leaks.c Normal file
View File

@ -0,0 +1,6 @@
#include <darwintest_utils.h>
T_DECL(notify_leaks, "checks for leaks in notifyd", T_META_ASROOT(true))
{
dt_check_leaks_by_name("notifyd");
}

100
tests/notify_many_dups.c Normal file
View File

@ -0,0 +1,100 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <notify.h>
#include <darwintest.h>
T_DECL(notify_many_dups,
"notify many duplicate registration test",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"),
T_META_IGNORECRASHES("notify_many_dups*"))
{
int t, n, i;
uint32_t status;
mach_port_t port = MACH_PORT_NULL;
const char *name = "com.apple.notify.many.dups.test";
n = 50000;
status = notify_register_mach_port(name, &port, 0, &t);
T_EXPECT_EQ_INT(status, NOTIFY_STATUS_OK, "notify_register_mach_port status == NOTIFY_STATUS_OK");
for (i = 1; i < n; i++)
{
status = notify_register_mach_port(name, &port, NOTIFY_REUSE, &t);
if (status != NOTIFY_STATUS_OK) {
T_FAIL("notify_register_mach_port status != NOTIFY_STATUS_OK (status: %d, iteration: %d", status, i);
}
}
T_PASS("Successfully registered %d times for name %s\n", n, name);
}
#define notify_many_dups_number 500
T_DECL(notify_many_dups_posts,
"notify many duplicate registration posting test",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"),
T_META_IGNORECRASHES("notify_many_dups*"))
{
int i;
uint32_t status;
mach_port_t port = MACH_PORT_NULL;
const char *name = "com.apple.notify.many.dups.post.test";
int tokens[notify_many_dups_number];
status = notify_register_mach_port(name, &port, 0, &tokens[0]);
T_EXPECT_EQ_INT(status, NOTIFY_STATUS_OK, "notify_register_mach_port status == NOTIFY_STATUS_OK");
for (i = 1; i < notify_many_dups_number; i++)
{
tokens[i] = NOTIFY_TOKEN_INVALID;
status = notify_register_mach_port(name, &port, NOTIFY_REUSE, &tokens[i]);
if (status != NOTIFY_STATUS_OK) {
T_FAIL("notify_register_mach_port status != NOTIFY_STATUS_OK (status: %d, iteration: %d)", status, i);
}
if (tokens[i] == NOTIFY_TOKEN_INVALID) {
T_FAIL("notify_register_mach_port did not set token (iteration: %d)", i);
}
}
T_PASS("Successfully registered %d times for name %s\n", notify_many_dups_number, name);
notify_post(name);
sleep(1);
while (true) {
int tid;
mach_msg_empty_rcv_t msg;
kern_return_t status;
memset(&msg, 0, sizeof(msg));
status = mach_msg(&msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg), port, 100, MACH_PORT_NULL);
if (status != KERN_SUCCESS) {
T_LOG("mach msg returned %d", status);
break;
}
tid = msg.header.msgh_id;
for (int j = 0; j < notify_many_dups_number; j++) {
if (tokens[j] == tid) {
T_PASS("Received message for registration %d (%d)", j, tokens[j]);
tokens[j] = 0;
notify_cancel(tid);
}
}
}
for (int k = 0; k < notify_many_dups_number; k++) {
if (tokens[k] != 0) {
notify_cancel(tokens[k]);
T_FAIL("Did not receive notification for %d (%d)", k, tokens[k]);
}
}
}

113
tests/notify_matching.c Normal file
View File

@ -0,0 +1,113 @@
#include <darwintest.h>
#include <darwintest_multiprocess.h>
#include <notify.h>
#include <xpc/private.h>
T_DECL(notify_matching, "notifyd.matching plist registration",
T_META_LAUNCHD_PLIST("com.apple.notifyd.test.matching.plist"))
{
xpc_set_event_stream_handler("com.apple.notifyd.matching", dispatch_get_main_queue(),
^(xpc_object_t event) {
T_ASSERT_EQ_STR(xpc_dictionary_get_string(event, "Notification"), "com.apple.notifyd.test.matching", NULL);
T_END;
});
uint32_t status = notify_post("com.apple.notifyd.test.matching");
T_ASSERT_EQ(status, NOTIFY_STATUS_OK, NULL);
dispatch_main();
}
T_DECL(notify_matching_state, "notifyd.matching plist registration with event that carries state",
T_META_LAUNCHD_PLIST("com.apple.notifyd.test.matching.plist"))
{
xpc_set_event_stream_handler("com.apple.notifyd.matching", dispatch_get_main_queue(),
^(xpc_object_t event) {
T_ASSERT_EQ_STR(xpc_dictionary_get_string(event, "Notification"), "com.apple.notifyd.test.matching", NULL);
T_ASSERT_EQ((int)xpc_dictionary_get_uint64(event, "_State"), 42, NULL);
T_END;
});
int token;
uint32_t status = notify_register_check("com.apple.notifyd.test.matching", &token);
T_ASSERT_EQ(status, NOTIFY_STATUS_OK, NULL);
status = notify_set_state(token, 42);
T_ASSERT_EQ(status, NOTIFY_STATUS_OK, NULL);
status = notify_post("com.apple.notifyd.test.matching");
T_ASSERT_EQ(status, NOTIFY_STATUS_OK, NULL);
dispatch_main();
}
T_HELPER_DECL(notify_matching_system_impl, "implementation for notify_matching_system")
{
xpc_set_event_stream_handler("com.apple.notifyd.matching", dispatch_get_main_queue(),
^(xpc_object_t event) {
T_ASSERT_EQ_STR(xpc_dictionary_get_string(event, "Notification"), "com.apple.notifyd.test.matching", NULL);
T_END;
});
uint32_t status = notify_post("com.apple.notifyd.test.matching");
T_ASSERT_EQ(status, NOTIFY_STATUS_OK, NULL);
dispatch_main();
}
T_DECL(notify_matching_system, "notifyd.matching plist registration in the system domain",
T_META_ASROOT(true))
{
dt_helper_t helpers[] = {
dt_launchd_helper_domain("com.apple.notifyd.test.matching.plist", "notify_matching_system_impl", NULL, LAUNCH_SYSTEM_DOMAIN)
};
dt_run_helpers(helpers, 1, 60);
}
T_DECL(notify_matching_runtime, "notifyd.matching at runtime",
T_META("launchd_plist", "RunAtLoad.plist"))
{
xpc_set_event_stream_handler("com.apple.notifyd.matching", dispatch_get_main_queue(),
^(xpc_object_t event) {
T_ASSERT_EQ_STR(xpc_dictionary_get_string(event, "Notification"), "com.apple.notifyd.test.matching_runtime", NULL);
T_END;
});
xpc_object_t registration = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(registration, "Notification", "com.apple.notifyd.test.matching_runtime");
xpc_set_event("com.apple.notifyd.matching", "foo", registration);
// Gross. xpc_set_event is asynchronous, wait for a few sec before posting
dispatch_after(_dispatch_time_after_sec(5), dispatch_get_main_queue(), ^{
uint32_t status = notify_post("com.apple.notifyd.test.matching_runtime");
T_ASSERT_EQ(status, NOTIFY_STATUS_OK, NULL);
});
dispatch_main();
}
T_DECL(notify_matching_uid, "notifyd.matching uid registration",
T_META("launchd_plist", "RunAtLoad.plist"))
{
char *name = NULL;
asprintf(&name, "user.uid.%d.com.apple.notifyd.test.matching_uid", getuid());
xpc_set_event_stream_handler("com.apple.notifyd.matching", dispatch_get_main_queue(),
^(xpc_object_t event) {
T_ASSERT_EQ_STR(xpc_dictionary_get_string(event, "Notification"), name, NULL);
T_END;
});
xpc_object_t registration = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(registration, "Notification", name);
xpc_set_event("com.apple.notifyd.matching", "foo", registration);
// Gross. xpc_set_event is asynchronous, wait for a few sec before posting
dispatch_after(_dispatch_time_after_sec(5), dispatch_get_main_queue(), ^{
uint32_t status = notify_post(name);
T_ASSERT_EQ(status, NOTIFY_STATUS_OK, NULL);
});
dispatch_main();
}

187
tests/notify_pathwatch.c Normal file
View File

@ -0,0 +1,187 @@
//
// notify_pathwatch.c
// tests
//
// Created by Brycen Wershing on 8/2/18.
//
#include <darwintest.h>
#include <dispatch/dispatch.h>
#include <notify.h>
#include <stdio.h>
#include <stdlib.h>
#include <mach/mach.h>
#include <darwintest_multiprocess.h>
#include "notify_private.h"
int dispatch_changer;
#define PATHWATCH_TEST_NAME "com.example.test.pathwatch"
#define PATHWATCH_TEST_FILE "/tmp/notify_pathwatch.test"
T_DECL(notify_pathwatch,
"notify pathwatch test",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "true"))
{
dispatch_queue_t testQueue = dispatch_queue_create("testQ", DISPATCH_QUEUE_SERIAL);
uint32_t rc;
static int check_token = NOTIFY_TOKEN_INVALID;
static int disp_token = NOTIFY_TOKEN_INVALID;
int check;
uint64_t state;
int fd, bytes_written;
dispatch_changer = 0;
// register for check and dispatch
rc = notify_register_check(PATHWATCH_TEST_NAME, &check_token);
assert(rc == NOTIFY_STATUS_OK);
rc = notify_check(check_token, &check);
assert(rc == NOTIFY_STATUS_OK);
assert(check == 1);
rc = notify_register_dispatch(PATHWATCH_TEST_NAME, &disp_token, testQueue, ^(int i){dispatch_changer++;});
assert(rc == NOTIFY_STATUS_OK);
// add file monitoring for nonexistant file
unlink(PATHWATCH_TEST_FILE); // return code ignored because the file probably does not exist
notify_monitor_file(check_token, PATHWATCH_TEST_FILE, 0x3ff);
notify_monitor_file(disp_token, PATHWATCH_TEST_FILE, 0x3ff);
// there should not be a post yet
dispatch_sync(testQueue, ^{assert(
dispatch_changer == 0);
});
rc = notify_check(check_token, &check);
assert(rc == NOTIFY_STATUS_OK);
assert(check == 0);
// post and fence
rc = notify_post(PATHWATCH_TEST_NAME);
assert(rc == NOTIFY_STATUS_OK);
notify_get_state(check_token, &state); //fence
sleep(1);
// check to make sure post was good
rc = notify_check(check_token, &check);
assert(rc == NOTIFY_STATUS_OK);
assert(check == 1);
dispatch_sync(testQueue, ^{
assert(dispatch_changer == 1);
dispatch_changer = 0;
});
// create the file
fd = creat(PATHWATCH_TEST_FILE, 0);
assert(fd != -1);
sleep(1);
notify_get_state(check_token, &state); //fence
// check to make sure post was good
rc = notify_check(check_token, &check);
assert(rc == NOTIFY_STATUS_OK);
assert(check == 1);
dispatch_sync(testQueue, ^{
assert(dispatch_changer == 1);
dispatch_changer = 0;
});
// write something to the file
bytes_written = write(fd, "this", 4);
assert(bytes_written == 4);
sleep(1);
notify_get_state(check_token, &state); //fence
// check to make sure post was good
rc = notify_check(check_token, &check);
assert(rc == NOTIFY_STATUS_OK);
assert(check == 1);
dispatch_sync(testQueue, ^{
assert(dispatch_changer == 1);
dispatch_changer = 0;
});
// delete the file
rc = close(fd);
assert(rc == 0);
unlink(PATHWATCH_TEST_FILE);
sleep(1);
notify_get_state(check_token, &state); //fence
// check to make sure post was good
rc = notify_check(check_token, &check);
assert(rc == NOTIFY_STATUS_OK);
assert(check == 1);
dispatch_sync(testQueue, ^{
assert(dispatch_changer == 1);
dispatch_changer = 0;
});
// cleanup
rc = notify_cancel(check_token);
assert(rc == NOTIFY_STATUS_OK);
rc = notify_cancel(disp_token);
assert(rc == NOTIFY_STATUS_OK);
dispatch_release(testQueue);
fd = creat(PATHWATCH_TEST_FILE, 0);
assert(fd != -1);
dt_helper_t helpers[50];
for(int i = 0; i < 50; i++){
helpers[i] = dt_child_helper("notify_pathwatch_helper");
}
dt_run_helpers(helpers, 50, 180);
rc = close(fd);
assert(rc == 0);
unlink(PATHWATCH_TEST_FILE);
T_PASS("Success");
}
T_HELPER_DECL(notify_pathwatch_helper,
"notify pathwatch test helper",
T_META("owner", "Core Darwin Daemons & Tools"))
{
int token;
char *filename;
uint32_t block_rc;
block_rc = notify_register_check(PATHWATCH_TEST_NAME, &token);
assert(block_rc == NOTIFY_STATUS_OK);
notify_monitor_file(token, PATHWATCH_TEST_FILE, 0x3ff);
T_PASS("Helper passed");
T_END;
exit(0);
}

71
tests/notify_qos.c Normal file
View File

@ -0,0 +1,71 @@
#include <stdlib.h>
#include <notify.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <darwintest.h>
#include <signal.h>
#define KEY "com.apple.notify.test.notify_register_signal"
#define HANDLED 1
#define UNHANDLED 0
// Notification token
int n_token;
// Global to show if notification was handled
int handled = 0;
void post_notification(char * msg, int should_handle) {
int i, loops = 5000; //5000*10 = 50,000 = 50ms
// Post notification
handled = UNHANDLED;
T_LOG("%s", msg);
notify_post(KEY);
for(i=0; i < loops; i++) {
if(handled == HANDLED)
break;
else
usleep(10);
}
if(should_handle)
T_EXPECT_EQ(handled, (should_handle ? HANDLED : UNHANDLED),
"Signal based notification handled as expected.");
}
void setup() {
// Register with notification system
dispatch_queue_t dq = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
int rv = notify_register_dispatch(KEY, &n_token, dq, ^(int token){
handled = HANDLED;
qos_class_t block_qos = qos_class_self();
if (block_qos != QOS_CLASS_DEFAULT){
T_FAIL("Block is running at QoS %#x instead of DEF", block_qos);
} else {
}
});
if (rv)
T_FAIL("Unable to notify_register_dispatch");
}
void cleanup() {
notify_cancel(n_token); /* Releases the queue - block must finish executing */
}
T_DECL(notify_qos,
"Test that work for notification runs at qos of target queue",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
setup();
post_notification("Ensure notifications are being handled properly.", 1);
cleanup();
}

129
tests/notify_regenerate.c Normal file
View File

@ -0,0 +1,129 @@
#include <stdlib.h>
#include <notify.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <darwintest.h>
#include <signal.h>
#include "../libnotify.h"
#define T_ASSERT_GROUP_WAIT(g, timeout, ...) ({ \
dispatch_time_t _timeout = dispatch_time(DISPATCH_TIME_NOW, \
(uint64_t)(timeout * NSEC_PER_SEC)); \
T_ASSERT_FALSE(dispatch_group_wait(g, _timeout), __VA_ARGS__); \
})
static bool has_port_been_notified;
static int m_token;
static void port_handler(mach_port_t port)
{
int tid;
mach_msg_empty_rcv_t msg;
kern_return_t status;
if (port == MACH_PORT_NULL) return;
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;
tid = msg.header.msgh_id;
T_ASSERT_EQ(m_token, tid, "port_handler should be called with mach_port token");
has_port_been_notified = true;
}
T_DECL(notify_regenerate, "Make sure regenerate registrations works",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META_ASROOT(YES))
{
const char *KEY = "com.apple.notify.test.regenerate";
int v_token, n_token, rc;
pid_t old_pid, new_pid;
uint64_t state;
mach_port_t watch_port;
dispatch_source_t port_src;
dispatch_queue_t watch_queue = dispatch_queue_create("Watch Q", NULL);
dispatch_queue_t dq = dispatch_queue_create_with_target("q", NULL, NULL);
dispatch_group_t dg = dispatch_group_create();
T_LOG("Grab the current instance pid & version");
{
rc = notify_register_check(NOTIFY_IPC_VERSION_NAME, &v_token);
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "register_check(NOTIFY_IPC_VERSION_NAME)");
state = ~0ull;
rc = notify_get_state(v_token, &state);
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "notify_get_state(NOTIFY_IPC_VERSION_NAME)");
old_pid = (pid_t)(state >> 32);
T_EXPECT_EQ((uint32_t)state, NOTIFY_IPC_VERSION, "IPC version should be set");
}
T_LOG("Register for our test topic, and check it works");
{
rc = notify_register_mach_port(KEY, &watch_port, 0, &m_token);
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "register mach port should work");
port_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, watch_port, 0, watch_queue);
dispatch_source_set_event_handler(port_src, ^{
port_handler(watch_port);
});
dispatch_activate(port_src);
rc = notify_register_dispatch(KEY, &n_token, dq, ^(int token){
// if we crash here, it means we got a spurious post
// e.g. if you run the test twice concurrently this could happen
dispatch_group_leave(dg);
});
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "register dispatch should work");
dispatch_group_enter(dg);
has_port_been_notified = false;
notify_post(KEY);
sleep(1);
T_ASSERT_GROUP_WAIT(dg, 5., "we received our own notification");
T_ASSERT_EQ(has_port_been_notified, true, "mach port should receive notification");
}
T_LOG("Make sure notifyd changes pid due to a kill");
{
state = ~0ull;
rc = notify_get_state(v_token, &state);
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "notify_get_state(NOTIFY_IPC_VERSION_NAME)");
new_pid = (pid_t)(state >> 32);
T_ASSERT_EQ(old_pid, new_pid, "Pid should not have changed yet");
rc = kill(old_pid, SIGKILL);
T_ASSERT_POSIX_SUCCESS(rc, "Killing notifyd");
new_pid = old_pid;
for (int i = 0; i < 10 && new_pid == old_pid; i++) {
usleep(100000); /* wait for .1s for notifyd to die */
state = ~0ull;
rc = notify_get_state(v_token, &state);
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "notify_get_state(NOTIFY_IPC_VERSION_NAME)");
new_pid = (pid_t)(state >> 32);
}
T_ASSERT_NE(old_pid, new_pid, "Pid should have changed within 1s");
}
T_LOG("Make sure our old registration works");
{
// No-dispatch mach port registrations don't regenerate
dispatch_group_enter(dg);
notify_post(KEY);
sleep(1);
T_ASSERT_GROUP_WAIT(dg, 5., "we received our own notification");
}
dispatch_release(port_src);
}

View File

@ -0,0 +1,32 @@
//
// notify_register_file_desc.c
//
// Created by Brycen Wershing on 6/29/20.
//
#include <stdlib.h>
#include <notify.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <darwintest.h>
#include <signal.h>
#include "../libnotify.h"
T_DECL(notify_register_file_desc, "Make sure mach port registrations works",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META_ASROOT(YES))
{
const char *KEY = "com.apple.notify.test.file_desc";
int rc, fd, tok;
rc = notify_register_file_descriptor(KEY, &fd, 0, &tok);
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "register file desc should work");
T_ASSERT_NE(fcntl(fd, F_GETFD), -1, "file descriptor should exist");
rc = notify_cancel(tok);
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "cancel should work");
T_ASSERT_EQ(fcntl(fd, F_GETFD), -1, "file descriptor should not exist");
}

View File

@ -0,0 +1,94 @@
//
// notify_register_mach_port.c
// Libnotify
//
// Created by Brycen Wershing on 10/23/19.
//
#include <stdlib.h>
#include <notify.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <darwintest.h>
#include <signal.h>
#include "../libnotify.h"
static bool has_port_been_notified_1, has_port_been_notified_2;
static int key_1_token, key_2_token;
static void port_handler(mach_port_t port)
{
int tid;
mach_msg_empty_rcv_t msg;
kern_return_t status;
if (port == MACH_PORT_NULL) return;
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;
tid = msg.header.msgh_id;
if (tid == key_1_token)
{
has_port_been_notified_1 = true;
}
else if (tid == key_2_token)
{
has_port_been_notified_2 = true;
}
else
{
T_FAIL("port handler should only be called with tokens %d and %d, but it was called with %d",
key_1_token, key_2_token, tid);
}
}
T_DECL(notify_register_mach_port, "Make sure mach port registrations works",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META_ASROOT(YES))
{
const char *KEY1 = "com.apple.notify.test.mach_port.1";
const char *KEY2 = "com.apple.notify.test.mach_port.2";
int rc;
// Make sure that posts without an existing registration don't do anything
// bad, like crash notifyd or break future posts
notify_post(KEY1);
notify_post(KEY1);
notify_post(KEY1);
mach_port_t watch_port;
dispatch_source_t port_src;
dispatch_queue_t watch_queue = dispatch_queue_create("Watch Q", NULL);
rc = notify_register_mach_port(KEY1, &watch_port, 0, &key_1_token);
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "register mach port should work");
rc = notify_register_mach_port(KEY2, &watch_port, NOTIFY_REUSE, &key_2_token);
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "register mach port should work");
port_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, watch_port, 0, watch_queue);
dispatch_source_set_event_handler(port_src, ^{
port_handler(watch_port);
});
dispatch_activate(port_src);
has_port_been_notified_1 = false;
has_port_been_notified_2 = false;
notify_post(KEY1);
sleep(1);
T_ASSERT_EQ(has_port_been_notified_1, true, "mach port 1 should receive notification");
T_ASSERT_EQ(has_port_been_notified_2, false, "mach port 2 should not receive notification");
has_port_been_notified_1 = false;
has_port_been_notified_2 = false;
notify_post(KEY2);
sleep(1);
T_ASSERT_EQ(has_port_been_notified_1, false, "mach port 1 should not receive notification");
T_ASSERT_EQ(has_port_been_notified_2, true, "mach port 2 should receive notification");
dispatch_release(port_src);
}

View File

@ -0,0 +1,163 @@
#include <stdlib.h>
#include <notify.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <darwintest.h>
#include <signal.h>
#define KEY "com.apple.notify.test.notify_register_signal"
void poster(char * msg);
#define HANDLED 1
#define UNHANDLED 0
// Notification token
int n_token;
// Global to show if notification was handled
int handled = 0;
void post_notification(char * msg, int should_handle) {
int i, loops = 100;
// Post notification
handled = 0;
T_LOG("%s", msg);
notify_post(KEY);
for(i=0; i < loops; i++) {
if(handled == HANDLED)
break;
else
usleep(10);
}
if(should_handle)
T_EXPECT_EQ(handled, (should_handle ? HANDLED : UNHANDLED),
"Signal based notification handled as expected.");
}
int check() {
int status;
uint32_t rv;
rv = notify_check(n_token, &status);
if(rv != NOTIFY_STATUS_OK)
T_FAIL("notify_check return value (%d) != NOTIFY_STATUS_OK", rv);
return status;
}
void sig_handler(int sig) {
int status = check();
// Only continue if handler was called from notification system
if(status < 1)
return;
if(handled != UNHANDLED)
T_FAIL("handled (%d) was not reset to UNHANDLED as expected.", handled);
handled = HANDLED;
}
void sig_setup() {
// Register our signal handler
signal(SIGINFO, sig_handler);
}
void sig_cleanup() {
// Register our signal handler
signal(SIGINFO, SIG_DFL);
}
void setup() {
int n_check;
// Setup our signal handler
sig_setup();
// Register with notification system
notify_register_signal(KEY, SIGINFO, &n_token);
n_check = check();
if(n_check != 1)
T_FAIL("notify_check failed to return 1 after initial registration.");
}
void cleanup() {
// Clean up our signal handler
sig_cleanup();
notify_cancel(n_token);
}
T_DECL(notify_register_signal_1,
"Basic test to confirm that notify_register_signal works.",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
sig_setup();
T_LOG("Registering for signal based notification.");
// Register with notification system
notify_register_signal(KEY, SIGINFO, &n_token);
post_notification("Ensure notifications are being handled properly.", 1);
sig_cleanup();
}
T_DECL(notify_register_signal_2,
"Test that notify_check handles signal notifications properly.",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
int n_check;
setup();
// Send UNIX style signal
kill(getpid(),SIGINFO);
T_EXPECT_EQ(handled, UNHANDLED,
"handled (%d) remains unhandled, as expected.", handled);
post_notification("Sighandler notification after kill()", 1);
cleanup();
}
T_DECL(notify_register_signal_3,
"Test that notify_resume and notify_suspend handle signal notifications properly.",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
int i, n_check;
uint32_t rv;
setup();
post_notification("Ensure notifications are being handled properly.", 1);
rv = notify_suspend(n_token);
T_EXPECT_EQ(rv, NOTIFY_STATUS_OK,
"notify_suspend rv (%d) != NOTIFY_STATUS_OK", rv);
for(i=0; i<3; i++) {
post_notification("Suspended notification", 0);
T_EXPECT_EQ(handled, UNHANDLED,
"handled (%d) remains unhandled, as expected.", handled);
}
// Returns NOTIFY_STATUS_FAILED. Leave off for now.
//rv = notify_resume(n_token);
//T_EXPECT_EQ(rv, NOTIFY_STATUS_OK,
// "notify_resume rv != NOTIFY_STATUS_OK");
//T_EXPECT_EQ(handled, HANDLED,
// "handled has been handed for suspended notifications");
cleanup();
}

67
tests/notify_sigusr.c Normal file
View File

@ -0,0 +1,67 @@
#include <stdlib.h>
#include <notify.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <darwintest.h>
#include <signal.h>
#include "../libnotify.h"
T_DECL(notify_sigusr, "Make sure SIGUSR{1,2} dumps status",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META_ASROOT(YES))
{
int v_token, rc;
pid_t pid;
uint64_t state;
T_LOG("Grab the current instance pid & version");
{
rc = notify_register_check(NOTIFY_IPC_VERSION_NAME, &v_token);
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "register_check(NOTIFY_IPC_VERSION_NAME)");
state = ~0ull;
rc = notify_get_state(v_token, &state);
T_ASSERT_EQ(rc, NOTIFY_STATUS_OK, "notify_get_state(NOTIFY_IPC_VERSION_NAME)");
pid = (pid_t)(state >> 32);
T_EXPECT_EQ((uint32_t)state, NOTIFY_IPC_VERSION, "IPC version should be set");
}
char *status_file;
asprintf(&status_file, "/var/run/notifyd_%u.status", pid);
T_LOG("Make sure SIGUSR1 works");
{
rc = kill(pid, SIGUSR1);
T_ASSERT_POSIX_SUCCESS(rc, "Killing notifyd");
rc = -1;
for (int i = 0; i < 10 && rc == -1; i++) {
rc = unlink(status_file);
if (rc == -1 && errno != ENOENT) {
T_ASSERT_POSIX_SUCCESS(rc, "unlink(%s)", status_file);
}
usleep(100000); /* wait for .1s for notifyd to dump status */
}
T_ASSERT_POSIX_SUCCESS(rc, "unlink(%s)", status_file);
}
T_LOG("Make sure SIGUSR2 works");
{
rc = kill(pid, SIGUSR2);
T_ASSERT_POSIX_SUCCESS(rc, "Killing notifyd");
rc = -1;
for (int i = 0; i < 10 && rc == -1; i++) {
rc = unlink(status_file);
if (rc == -1 && errno != ENOENT) {
T_ASSERT_POSIX_SUCCESS(rc, "unlink(%s)", status_file);
}
usleep(100000); /* wait for .1s for notifyd to dump status */
}
T_ASSERT_POSIX_SUCCESS(rc, "unlink(%s)", status_file);
}
free(status_file);
}

65
tests/notify_suspend.c Normal file
View File

@ -0,0 +1,65 @@
//
// notify_suspend.c
// Libnotify
//
// Created by Brycen Wershing on 10/16/19.
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <notify.h>
#include <darwintest.h>
T_DECL(notify_suspend,
"notify suspend registration test",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
__block bool suspendedToggle = false;
__block bool notSuspendedToggle = false;
int status;
int suspendToken, noSuspendToken;
const char *name = "com.apple.test.suspend";
dispatch_queue_t queue = dispatch_queue_create("Test Q", NULL);
status = notify_register_dispatch(name, &suspendToken, queue, ^(__unused int token){
suspendedToggle = true;
});
T_ASSERT_EQ(status, NOTIFY_STATUS_OK, "Register suspend token");
status = notify_register_dispatch(name, &noSuspendToken, queue, ^(__unused int token){
notSuspendedToggle = true;
});
T_ASSERT_EQ(status, NOTIFY_STATUS_OK, "Register no-suspend token");
sleep(1);
T_ASSERT_EQ(suspendedToggle, false, "check suspend token before posts");
T_ASSERT_EQ(notSuspendedToggle, false, "check no-suspend token before posts");
notify_suspend(suspendToken);
notify_post(name);
sleep(1);
T_ASSERT_EQ(suspendedToggle, false, "check suspend token after post");
T_ASSERT_EQ(notSuspendedToggle, true, "check no-suspend token after post");
suspendedToggle = false;
notSuspendedToggle = false;
notify_resume(suspendToken);
sleep(1);
T_ASSERT_EQ(suspendedToggle, true, "check suspend token after resume");
T_ASSERT_EQ(notSuspendedToggle, false, "check no-suspend token after resume");
notify_post(name);
sleep(1);
T_ASSERT_EQ(suspendedToggle, true, "check suspend token after final post");
T_ASSERT_EQ(notSuspendedToggle, true, "check no-suspend token after final post");
T_PASS("Test complete");
}

102
tests/notify_test_helper.c Normal file
View File

@ -0,0 +1,102 @@
//
// notify_exec_helper.c
// darwintests
//
// Created by Brycen Wershing on 2/10/20.
//
#include <stdio.h>
#include <stdlib.h>
#include <notify.h>
#include <unistd.h>
#include <errno.h>
extern char **environ;
#define KEY "com.apple.notify.test"
int main(int argc, char *argv[]) {
printf("In notify_test_helper\n");
uint32_t rc;
int c_token, d_token;
int check;
__block bool dispatch_called = false;
dispatch_queue_t dq = dispatch_queue_create_with_target("q", NULL, NULL);
rc = notify_register_check(KEY, &c_token);
if (rc != NOTIFY_STATUS_OK)
{
return 51;
}
notify_check(c_token, &check);
if (check != 1) {
return 52;
}
notify_check(c_token, &check);
if (check != 0) {
return 53;
}
rc = notify_register_dispatch(KEY, &d_token, dq, ^(int token){
dispatch_called = true;
});
if (rc != NOTIFY_STATUS_OK)
{
return 54;
}
sleep(1);
if (dispatch_called) {
return 55;
}
rc = notify_post(KEY);
if (rc != NOTIFY_STATUS_OK)
{
return 56;
}
sleep(1);
notify_check(c_token, &check);
if (check != 1) {
return 57;
}
if (!dispatch_called) {
return 58;
}
rc = notify_cancel(c_token);
if (rc != NOTIFY_STATUS_OK)
{
return 59;
}
rc = notify_cancel(d_token);
if (rc != NOTIFY_STATUS_OK)
{
return 60;
}
if (argc == 2) {
printf("Calling exec again\n");
char *argv[2];
argv[0] = "notify_test_helper";
argv[1] = NULL;
execve("/AppleInternal/Tests/Libnotify/notify_test_helper" ,argv , environ);
printf("execve failed with %d\n", errno);
abort();
}
printf("Succeeded!\n");
return 42;
}

View File

@ -0,0 +1,36 @@
//
// parallel_register_cancel.c
// Libnotify
//
#include <darwintest.h>
#include <dispatch/dispatch.h>
#include <notify.h>
#include <stdio.h>
T_DECL(parallel_register_cancel,
"parallel register/cancel test",
T_META("owner", "Core Darwin Daemons & Tools"),
T_META("as_root", "false"))
{
dispatch_queue_t noteQueue = dispatch_queue_create("noteQ", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
static int tokens[100000];
dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) {
assert(notify_register_check("com.example.test", &tokens[i]) == NOTIFY_STATUS_OK);
assert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK);
assert(notify_register_dispatch("com.example.test", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK);
assert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK);
assert(notify_post("com.example.test") == NOTIFY_STATUS_OK);
});
dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) {
assert(notify_register_check("self.example.test", &tokens[i]) == NOTIFY_STATUS_OK);
assert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK);
assert(notify_register_dispatch("self.example.test", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK);
assert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK);
assert(notify_post("self.example.test") == NOTIFY_STATUS_OK);
});
T_PASS("Success");
}

259
tests/random_test.c Normal file
View File

@ -0,0 +1,259 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <notify.h>
#include <dispatch/dispatch.h>
#define forever for(;;)
#define MAX_REG 1000
#define QUIT 1000000
int token[MAX_REG];
char *tname[MAX_REG];
int nregs = 0;
pid_t mypid;
int scount = 0;
uint64_t actions = 0;
uint32_t
pick_token(uint32_t x)
{
uint32_t i, n;
if (nregs == 0) return MAX_REG;
i = (x / 100) % MAX_REG;
for (n = 0; n < MAX_REG; n++)
{
if (token[i] >= 0) return i;
i = (i + i) % MAX_REG;
}
return MAX_REG;
}
uint32_t
empty_token(uint32_t x)
{
uint32_t i, n;
if (nregs == MAX_REG) return MAX_REG;
i = (x / 100) % MAX_REG;
for (n = 0; n < MAX_REG; n++)
{
if (token[i] < 0) return i;
i = (i + i) % MAX_REG;
}
return MAX_REG;
}
char *
random_name(uint32_t x)
{
char *s = NULL;
asprintf(&s, "Random.Notify.Test.%u.%u", mypid, x);
return s;
}
void
do_something()
{
printf(".");
fflush(stdout);
scount++;
if (actions >= QUIT)
{
printf("\nTest Complete\n");
exit(0);
}
if ((scount % 100) == 0) printf("\n%20llu %10u ", actions, scount);
}
/* 0 */
void
random_cancel(uint32_t x)
{
uint32_t i = pick_token(x);
if (i >= MAX_REG) return;
notify_cancel(token[i]);
free(tname[i]);
tname[i] = NULL;
token[i] = -1;
nregs--;
}
/* 1 */
void
random_register_check(uint32_t x)
{
int t;
uint32_t i = empty_token(x);
if (i >= MAX_REG) return;
char *s = random_name(x);
if (s == NULL) return;
if (notify_register_check(s, &t) == NOTIFY_STATUS_OK)
{
token[i] = t;
tname[i] = s;
nregs++;
}
else
{
free(s);
}
}
/* 2 */
void
random_register_dispatch(uint32_t x)
{
int t;
uint32_t i = empty_token(x);
if (i >= MAX_REG) return;
char *s = random_name(x);
if (s == NULL) return;
if (notify_register_dispatch(s, &t, dispatch_get_main_queue(), ^(int x){ do_something(); }) == NOTIFY_STATUS_OK)
{
token[i] = t;
tname[i] = s;
nregs++;
}
else
{
free(s);
}
}
/* 3 */
void
random_register_plain(uint32_t x)
{
int t;
uint32_t i = empty_token(x);
if (i >= MAX_REG) return;
char *s = random_name(x);
if (s == NULL) return;
if (notify_register_plain(s, &t) == NOTIFY_STATUS_OK)
{
token[i] = t;
tname[i] = s;
nregs++;
}
else
{
free(s);
}
}
/* 4 */
void
random_post(uint32_t x)
{
uint32_t i;
if (0 == (x % 10))
{
char *s = random_name(x);
notify_post(s);
free(s);
return;
}
i = pick_token(x);
if (i >= MAX_REG) return;
notify_post(tname[i]);
}
/* 5 */
void
random_check(uint32_t x)
{
int c;
uint32_t i = pick_token(x);
if (i >= MAX_REG) return;
notify_check(token[i], &c);
}
/* 6 */
void
random_get_state(uint32_t x)
{
uint64_t v;
uint32_t i = pick_token(x);
if (i >= MAX_REG) return;
notify_get_state(token[i], &v);
}
/* 7 */
void
random_set_state(uint32_t x)
{
uint64_t v = x;
uint32_t i = pick_token(x);
if (i >= MAX_REG) return;
notify_set_state(token[i], v);
}
void
random_notify()
{
uint32_t x, act, nap;
forever
{
x = arc4random();
nap = ((x / 10) % 10) * 1000;
act = x % 8;
switch (act)
{
case 0: random_cancel(x); break;
case 1: random_register_check(x); break;
case 2: random_register_dispatch(x); break;
case 3: random_register_plain(x); break;
case 4: random_post(x); break;
case 5: random_check(x); break;
case 6: random_get_state(x); break;
case 7: random_set_state(x); break;
default: break;
}
usleep(nap);
actions++;
}
}
int
main(int argc, char *argv[])
{
uint32_t i;
for (i = 0; i < MAX_REG; i++) token[i] = -1;
// dispatch_queue_t randomq = dispatch_queue_create("Random Queue", NULL);
dispatch_queue_t randomq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
mypid = getpid();
printf("%20u %10u ", 0, 0);
dispatch_async(randomq, ^{ random_notify(); });
dispatch_main();
return 0;
}

View File

@ -3,7 +3,7 @@
// Architectures
SDKROOT = macosx.internal
ALWAYS_SEARCH_USER_PATHS = YES
ALWAYS_SEARCH_USER_PATHS = NO
HEADER_SEARCH_PATHS = $(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders $(PROJECT_DIR)
ARCHS = $(ARCHS_STANDARD_32_64_BIT)
CODE_SIGN_IDENTITY = -
@ -15,10 +15,10 @@ GCC_PREPROCESSOR_DEFINITIONS = __DARWIN_NON_CANCELABLE=1
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 -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
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES
GCC_WARN_ABOUT_RETURN_TYPE = YES
OTHER_CFLAGS = -fno-exceptions -Weverything -Wno-reserved-id-macro -Wno-padded -Wno-used-but-marked-unused -Wno-undef -Wno-cast-qual -Wno-unused-macros -Wno-gnu -Wno-fixed-enum-extension -Wno-c++98-compat -Wno-pedantic
OTHER_CFLAGS_debug = -O0
CURRENT_PROJECT_VERSION =
CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion)
@ -31,3 +31,4 @@ 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

0
xcodescripts/no-sim-man.sh Normal file → Executable file
View File

View File

@ -1,5 +0,0 @@
#!/bin/bash -ex
if [[ "${PLATFORM_NAME}" =~ "simulator" ]]; then
ln -s libsystem_notify.dylib ${DSTROOT}${INSTALL_PATH}/libnotify_sim.dylib
fi