mirror of
https://github.com/darlinghq/darling-Libnotify.git
synced 2024-11-23 04:09:49 +00:00
Transfer Libnotify code from main repo
Based on `Libnotify-241`
This commit is contained in:
commit
2526674fb5
372
APPLE_LICENSE
Normal file
372
APPLE_LICENSE
Normal file
@ -0,0 +1,372 @@
|
||||
APPLE PUBLIC SOURCE LICENSE
|
||||
Version 1.1 - April 19,1999
|
||||
|
||||
Please read this License carefully before downloading this software.
|
||||
By downloading and using this software, you are agreeing to be bound
|
||||
by the terms of this License. If you do not or cannot agree to the
|
||||
terms of this License, please do not download or use the software.
|
||||
|
||||
1. General; Definitions. This License applies to any program or other
|
||||
work which Apple Computer, Inc. ("Apple") publicly announces as
|
||||
subject to this Apple Public Source License and which contains a
|
||||
notice placed by Apple identifying such program or work as "Original
|
||||
Code" and stating that it is subject to the terms of this Apple Public
|
||||
Source License version 1.1 (or subsequent version thereof), as it may
|
||||
be revised from time to time by Apple ("License"). As used in this
|
||||
License:
|
||||
|
||||
1.1 "Affected Original Code" means only those specific portions of
|
||||
Original Code that allegedly infringe upon any party's intellectual
|
||||
property rights or are otherwise the subject of a claim of
|
||||
infringement.
|
||||
|
||||
1.2 "Applicable Patent Rights" mean: (a) in the case where Apple is
|
||||
the grantor of rights, (i) claims of patents that are now or hereafter
|
||||
acquired, owned by or assigned to Apple and (ii) that cover subject
|
||||
matter contained in the Original Code, but only to the extent
|
||||
necessary to use, reproduce and/or distribute the Original Code
|
||||
without infringement; and (b) in the case where You are the grantor of
|
||||
rights, (i) claims of patents that are now or hereafter acquired,
|
||||
owned by or assigned to You and (ii) that cover subject matter in Your
|
||||
Modifications, taken alone or in combination with Original Code.
|
||||
|
||||
1.3 "Covered Code" means the Original Code, Modifications, the
|
||||
combination of Original Code and any Modifications, and/or any
|
||||
respective portions thereof.
|
||||
|
||||
1.4 "Deploy" means to use, sublicense or distribute Covered Code other
|
||||
than for Your internal research and development (R&D), and includes
|
||||
without limitation, any and all internal use or distribution of
|
||||
Covered Code within Your business or organization except for R&D use,
|
||||
as well as direct or indirect sublicensing or distribution of Covered
|
||||
Code by You to any third party in any form or manner.
|
||||
|
||||
1.5 "Larger Work" means a work which combines Covered Code or portions
|
||||
thereof with code not governed by the terms of this License.
|
||||
|
||||
1.6 "Modifications" mean any addition to, deletion from, and/or change
|
||||
to, the substance and/or structure of Covered Code. When code is
|
||||
released as a series of files, a Modification is: (a) any addition to
|
||||
or deletion from the contents of a file containing Covered Code;
|
||||
and/or (b) any new file or other representation of computer program
|
||||
statements that contains any part of Covered Code.
|
||||
|
||||
1.7 "Original Code" means (a) the Source Code of a program or other
|
||||
work as originally made available by Apple under this License,
|
||||
including the Source Code of any updates or upgrades to such programs
|
||||
or works made available by Apple under this License, and that has been
|
||||
expressly identified by Apple as such in the header file(s) of such
|
||||
work; and (b) the object code compiled from such Source Code and
|
||||
originally made available by Apple under this License.
|
||||
|
||||
1.8 "Source Code" means the human readable form of a program or other
|
||||
work that is suitable for making modifications to it, including all
|
||||
modules it contains, plus any associated interface definition files,
|
||||
scripts used to control compilation and installation of an executable
|
||||
(object code).
|
||||
|
||||
1.9 "You" or "Your" means an individual or a legal entity exercising
|
||||
rights under this License. For legal entities, "You" or "Your"
|
||||
includes any entity which controls, is controlled by, or is under
|
||||
common control with, You, where "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of fifty percent
|
||||
(50%) or more of the outstanding shares or beneficial ownership of
|
||||
such entity.
|
||||
|
||||
2. Permitted Uses; Conditions & Restrictions. Subject to the terms
|
||||
and conditions of this License, Apple hereby grants You, effective on
|
||||
the date You accept this License and download the Original Code, a
|
||||
world-wide, royalty-free, non- exclusive license, to the extent of
|
||||
Apple's Applicable Patent Rights and copyrights covering the Original
|
||||
Code, to do the following:
|
||||
|
||||
2.1 You may use, copy, modify and distribute Original Code, with or
|
||||
without Modifications, solely for Your internal research and
|
||||
development, provided that You must in each instance:
|
||||
|
||||
(a) retain and reproduce in all copies of Original Code the copyright
|
||||
and other proprietary notices and disclaimers of Apple as they appear
|
||||
in the Original Code, and keep intact all notices in the Original Code
|
||||
that refer to this License;
|
||||
|
||||
(b) include a copy of this License with every copy of Source Code of
|
||||
Covered Code and documentation You distribute, and You may not offer
|
||||
or impose any terms on such Source Code that alter or restrict this
|
||||
License or the recipients' rights hereunder, except as permitted under
|
||||
Section 6; and
|
||||
|
||||
(c) completely and accurately document all Modifications that you have
|
||||
made and the date of each such Modification, designate the version of
|
||||
the Original Code you used, prominently include a file carrying such
|
||||
information with the Modifications, and duplicate the notice in
|
||||
Exhibit A in each file of the Source Code of all such Modifications.
|
||||
|
||||
2.2 You may Deploy Covered Code, provided that You must in each
|
||||
instance:
|
||||
|
||||
(a) satisfy all the conditions of Section 2.1 with respect to the
|
||||
Source Code of the Covered Code;
|
||||
|
||||
(b) make all Your Deployed Modifications publicly available in Source
|
||||
Code form via electronic distribution (e.g. download from a web site)
|
||||
under the terms of this License and subject to the license grants set
|
||||
forth in Section 3 below, and any additional terms You may choose to
|
||||
offer under Section 6. You must continue to make the Source Code of
|
||||
Your Deployed Modifications available for as long as you Deploy the
|
||||
Covered Code or twelve (12) months from the date of initial
|
||||
Deployment, whichever is longer;
|
||||
|
||||
(c) if You Deploy Covered Code containing Modifications made by You,
|
||||
inform others of how to obtain those Modifications by filling out and
|
||||
submitting the information found at
|
||||
http://www.apple.com/publicsource/modifications.html, if available;
|
||||
and
|
||||
|
||||
(d) if You Deploy Covered Code in object code, executable form only,
|
||||
include a prominent notice, in the code itself as well as in related
|
||||
documentation, stating that Source Code of the Covered Code is
|
||||
available under the terms of this License with information on how and
|
||||
where to obtain such Source Code.
|
||||
|
||||
3. Your Grants. In consideration of, and as a condition to, the
|
||||
licenses granted to You under this License:
|
||||
|
||||
(a) You hereby grant to Apple and all third parties a non-exclusive,
|
||||
royalty-free license, under Your Applicable Patent Rights and other
|
||||
intellectual property rights owned or controlled by You, to use,
|
||||
reproduce, modify, distribute and Deploy Your Modifications of the
|
||||
same scope and extent as Apple's licenses under Sections 2.1 and 2.2;
|
||||
and
|
||||
|
||||
(b) You hereby grant to Apple and its subsidiaries a non-exclusive,
|
||||
worldwide, royalty-free, perpetual and irrevocable license, under Your
|
||||
Applicable Patent Rights and other intellectual property rights owned
|
||||
or controlled by You, to use, reproduce, execute, compile, display,
|
||||
perform, modify or have modified (for Apple and/or its subsidiaries),
|
||||
sublicense and distribute Your Modifications, in any form, through
|
||||
multiple tiers of distribution.
|
||||
|
||||
4. Larger Works. You may create a Larger Work by combining Covered
|
||||
Code with other code not governed by the terms of this License and
|
||||
distribute the Larger Work as a single product. In each such
|
||||
instance, You must make sure the requirements of this License are
|
||||
fulfilled for the Covered Code or any portion thereof.
|
||||
|
||||
5. Limitations on Patent License. Except as expressly stated in
|
||||
Section 2, no other patent rights, express or implied, are granted by
|
||||
Apple herein. Modifications and/or Larger Works may require
|
||||
additional patent licenses from Apple which Apple may grant in its
|
||||
sole discretion.
|
||||
|
||||
6. Additional Terms. You may choose to offer, and to charge a fee
|
||||
for, warranty, support, indemnity or liability obligations and/or
|
||||
other rights consistent with the scope of the license granted herein
|
||||
("Additional Terms") to one or more recipients of Covered
|
||||
Code. However, You may do so only on Your own behalf and as Your sole
|
||||
responsibility, and not on behalf of Apple. You must obtain the
|
||||
recipient's agreement that any such Additional Terms are offered by
|
||||
You alone, and You hereby agree to indemnify, defend and hold Apple
|
||||
harmless for any liability incurred by or claims asserted against
|
||||
Apple by reason of any such Additional Terms.
|
||||
|
||||
7. Versions of the License. Apple may publish revised and/or new
|
||||
versions of this License from time to time. Each version will be
|
||||
given a distinguishing version number. Once Original Code has been
|
||||
published under a particular version of this License, You may continue
|
||||
to use it under the terms of that version. You may also choose to use
|
||||
such Original Code under the terms of any subsequent version of this
|
||||
License published by Apple. No one other than Apple has the right to
|
||||
modify the terms applicable to Covered Code created under this
|
||||
License.
|
||||
|
||||
8. NO WARRANTY OR SUPPORT. The Original Code may contain in whole or
|
||||
in part pre-release, untested, or not fully tested works. The
|
||||
Original Code may contain errors that could cause failures or loss of
|
||||
data, and may be incomplete or contain inaccuracies. You expressly
|
||||
acknowledge and agree that use of the Original Code, or any portion
|
||||
thereof, is at Your sole and entire risk. THE ORIGINAL CODE IS
|
||||
PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND
|
||||
AND APPLE AND APPLE'S LICENSOR(S) (FOR THE PURPOSES OF SECTIONS 8 AND
|
||||
9, APPLE AND APPLE'S LICENSOR(S) ARE COLLECTIVELY REFERRED TO AS
|
||||
"APPLE") EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
AND/OR CONDITIONS OF MERCHANTABILITY OR SATISFACTORY QUALITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
|
||||
RIGHTS. APPLE DOES NOT WARRANT THAT THE FUNCTIONS CONTAINED IN THE
|
||||
ORIGINAL CODE WILL MEET YOUR REQUIREMENTS, OR THAT THE OPERATION OF
|
||||
THE ORIGINAL CODE WILL BE UNINTERRUPTED OR ERROR- FREE, OR THAT
|
||||
DEFECTS IN THE ORIGINAL CODE WILL BE CORRECTED. NO ORAL OR WRITTEN
|
||||
INFORMATION OR ADVICE GIVEN BY APPLE OR AN APPLE AUTHORIZED
|
||||
REPRESENTATIVE SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE
|
||||
SCOPE OF THIS WARRANTY. You acknowledge that the Original Code is not
|
||||
intended for use in the operation of nuclear facilities, aircraft
|
||||
navigation, communication systems, or air traffic control machines in
|
||||
which case the failure of the Original Code could lead to death,
|
||||
personal injury, or severe physical or environmental damage.
|
||||
|
||||
9. Liability.
|
||||
|
||||
9.1 Infringement. If any portion of, or functionality implemented by,
|
||||
the Original Code becomes the subject of a claim of infringement,
|
||||
Apple may, at its option: (a) attempt to procure the rights necessary
|
||||
for Apple and You to continue using the Affected Original Code; (b)
|
||||
modify the Affected Original Code so that it is no longer infringing;
|
||||
or (c) suspend Your rights to use, reproduce, modify, sublicense and
|
||||
distribute the Affected Original Code until a final determination of
|
||||
the claim is made by a court or governmental administrative agency of
|
||||
competent jurisdiction and Apple lifts the suspension as set forth
|
||||
below. Such suspension of rights will be effective immediately upon
|
||||
Apple's posting of a notice to such effect on the Apple web site that
|
||||
is used for implementation of this License. Upon such final
|
||||
determination being made, if Apple is legally able, without the
|
||||
payment of a fee or royalty, to resume use, reproduction,
|
||||
modification, sublicensing and distribution of the Affected Original
|
||||
Code, Apple will lift the suspension of rights to the Affected
|
||||
Original Code by posting a notice to such effect on the Apple web site
|
||||
that is used for implementation of this License. If Apple suspends
|
||||
Your rights to Affected Original Code, nothing in this License shall
|
||||
be construed to restrict You, at Your option and subject to applicable
|
||||
law, from replacing the Affected Original Code with non-infringing
|
||||
code or independently negotiating for necessary rights from such third
|
||||
party.
|
||||
|
||||
9.2 LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES SHALL APPLE BE
|
||||
LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES
|
||||
ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO
|
||||
USE THE ORIGINAL CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY
|
||||
OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY
|
||||
OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF
|
||||
ANY REMEDY. In no event shall Apple's total liability to You for all
|
||||
damages under this License exceed the amount of fifty dollars
|
||||
($50.00).
|
||||
|
||||
10. Trademarks. This License does not grant any rights to use the
|
||||
trademarks or trade names "Apple", "Apple Computer", "Mac OS X", "Mac
|
||||
OS X Server" or any other trademarks or trade names belonging to Apple
|
||||
(collectively "Apple Marks") and no Apple Marks may be used to endorse
|
||||
or promote products derived from the Original Code other than as
|
||||
permitted by and in strict compliance at all times with Apple's third
|
||||
party trademark usage guidelines which are posted at
|
||||
http://www.apple.com/legal/guidelinesfor3rdparties.html.
|
||||
|
||||
11. Ownership. Apple retains all rights, title and interest in and to
|
||||
the Original Code and any Modifications made by or on behalf of Apple
|
||||
("Apple Modifications"), and such Apple Modifications will not be
|
||||
automatically subject to this License. Apple may, at its sole
|
||||
discretion, choose to license such Apple Modifications under this
|
||||
License, or on different terms from those contained in this License or
|
||||
may choose not to license them at all. Apple's development, use,
|
||||
reproduction, modification, sublicensing and distribution of Covered
|
||||
Code will not be subject to this License.
|
||||
|
||||
12. Termination.
|
||||
|
||||
12.1 Termination. This License and the rights granted hereunder will
|
||||
terminate:
|
||||
|
||||
(a) automatically without notice from Apple if You fail to comply with
|
||||
any term(s) of this License and fail to cure such breach within 30
|
||||
days of becoming aware of such breach; (b) immediately in the event of
|
||||
the circumstances described in Section 13.5(b); or (c) automatically
|
||||
without notice from Apple if You, at any time during the term of this
|
||||
License, commence an action for patent infringement against Apple.
|
||||
|
||||
12.2 Effect of Termination. Upon termination, You agree to
|
||||
immediately stop any further use, reproduction, modification,
|
||||
sublicensing and distribution of the Covered Code and to destroy all
|
||||
copies of the Covered Code that are in your possession or control.
|
||||
All sublicenses to the Covered Code which have been properly granted
|
||||
prior to termination shall survive any termination of this License.
|
||||
Provisions which, by their nature, should remain in effect beyond the
|
||||
termination of this License shall survive, including but not limited
|
||||
to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. Neither party will be
|
||||
liable to the other for compensation, indemnity or damages of any sort
|
||||
solely as a result of terminating this License in accordance with its
|
||||
terms, and termination of this License will be without prejudice to
|
||||
any other right or remedy of either party.
|
||||
|
||||
13. Miscellaneous.
|
||||
|
||||
13.1 Government End Users. The Covered Code is a "commercial item" as
|
||||
defined in FAR 2.101. Government software and technical data rights
|
||||
in the Covered Code include only those rights customarily provided to
|
||||
the public as defined in this License. This customary commercial
|
||||
license in technical data and software is provided in accordance with
|
||||
FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for
|
||||
Department of Defense purchases, DFAR 252.227-7015 (Technical Data --
|
||||
Commercial Items) and 227.7202-3 (Rights in Commercial Computer
|
||||
Software or Computer Software Documentation). Accordingly, all U.S.
|
||||
Government End Users acquire Covered Code with only those rights set
|
||||
forth herein.
|
||||
|
||||
13.2 Relationship of Parties. This License will not be construed as
|
||||
creating an agency, partnership, joint venture or any other form of
|
||||
legal association between You and Apple, and You will not represent to
|
||||
the contrary, whether expressly, by implication, appearance or
|
||||
otherwise.
|
||||
|
||||
13.3 Independent Development. Nothing in this License will impair
|
||||
Apple's right to acquire, license, develop, have others develop for
|
||||
it, market and/or distribute technology or products that perform the
|
||||
same or similar functions as, or otherwise compete with,
|
||||
Modifications, Larger Works, technology or products that You may
|
||||
develop, produce, market or distribute.
|
||||
|
||||
13.4 Waiver; Construction. Failure by Apple to enforce any provision
|
||||
of this License will not be deemed a waiver of future enforcement of
|
||||
that or any other provision. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
will not apply to this License.
|
||||
|
||||
13.5 Severability. (a) If for any reason a court of competent
|
||||
jurisdiction finds any provision of this License, or portion thereof,
|
||||
to be unenforceable, that provision of the License will be enforced to
|
||||
the maximum extent permissible so as to effect the economic benefits
|
||||
and intent of the parties, and the remainder of this License will
|
||||
continue in full force and effect. (b) Notwithstanding the foregoing,
|
||||
if applicable law prohibits or restricts You from fully and/or
|
||||
specifically complying with Sections 2 and/or 3 or prevents the
|
||||
enforceability of either of those Sections, this License will
|
||||
immediately terminate and You must immediately discontinue any use of
|
||||
the Covered Code and destroy all copies of it that are in your
|
||||
possession or control.
|
||||
|
||||
13.6 Dispute Resolution. Any litigation or other dispute resolution
|
||||
between You and Apple relating to this License shall take place in the
|
||||
Northern District of California, and You and Apple hereby consent to
|
||||
the personal jurisdiction of, and venue in, the state and federal
|
||||
courts within that District with respect to this License. The
|
||||
application of the United Nations Convention on Contracts for the
|
||||
International Sale of Goods is expressly excluded.
|
||||
|
||||
13.7 Entire Agreement; Governing Law. This License constitutes the
|
||||
entire agreement between the parties with respect to the subject
|
||||
matter hereof. This License shall be governed by the laws of the
|
||||
United States and the State of California, except that body of
|
||||
California law concerning conflicts of law.
|
||||
|
||||
Where You are located in the province of Quebec, Canada, the following
|
||||
clause applies: The parties hereby confirm that they have requested
|
||||
that this License and all related documents be drafted in English. Les
|
||||
parties ont exige que le present contrat et tous les documents
|
||||
connexes soient rediges en anglais.
|
||||
|
||||
EXHIBIT A.
|
||||
|
||||
"Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
|
||||
Reserved. This file contains Original Code and/or Modifications of
|
||||
Original Code as defined in and that are subject to the Apple Public
|
||||
Source License Version 1.1 (the "License"). You may not use this file
|
||||
except in compliance with the License. Please obtain a copy of the
|
||||
License at http://www.apple.com/publicsource 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 OR NON- INFRINGEMENT. Please see the
|
||||
License for the specific language governing rights and limitations
|
||||
under the License."
|
47
CMakeLists.txt
Normal file
47
CMakeLists.txt
Normal file
@ -0,0 +1,47 @@
|
||||
project(libnotify)
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_definitions(-nostdinc -w)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks -include ${CMAKE_SOURCE_DIR}/src/external/lkm/bsd/sys/fileport.h")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib")
|
||||
|
||||
mig(notify_ipc.defs)
|
||||
|
||||
set(notify_sources
|
||||
libnotify.c
|
||||
notify_client.c
|
||||
table.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/notify_ipcUser.c
|
||||
)
|
||||
|
||||
set(DYLIB_INSTALL_NAME "/usr/lib/system/libsystem_notify.dylib")
|
||||
add_circular(system_notify FAT
|
||||
SOURCES
|
||||
${notify_sources}
|
||||
DEPENDENCIES
|
||||
system_c
|
||||
system_kernel
|
||||
system_blocks
|
||||
libdispatch_shared
|
||||
launch
|
||||
system_dyld
|
||||
system_malloc
|
||||
system_pthread
|
||||
platform
|
||||
system_asl
|
||||
xpc
|
||||
)
|
||||
#target_link_libraries(system_notify system_c system_kernel system_blocks libdispatch_shared launch system_dyld system_malloc system_pthread)
|
||||
|
||||
add_darling_executable(notifyutil notifyutil/notifyutil.c)
|
||||
|
||||
install(TARGETS system_notify DESTINATION libexec/darling/usr/lib/system)
|
||||
install(TARGETS notifyutil DESTINATION libexec/darling/usr/bin)
|
||||
install(FILES notifyutil/notifyutil.1 DESTINATION libexec/darling/usr/share/man/man1)
|
||||
|
||||
add_subdirectory(notifyd)
|
1241
Libnotify.xcodeproj/project.pbxproj
Normal file
1241
Libnotify.xcodeproj/project.pbxproj
Normal file
File diff suppressed because it is too large
Load Diff
72
Libnotify.xcodeproj/xcshareddata/xcschemes/xctests.xcscheme
Normal file
72
Libnotify.xcodeproj/xcshareddata/xcschemes/xctests.xcscheme
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1000"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "NO">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "NO"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94099C722087E7D50004B6BC"
|
||||
BuildableName = "xctests.xctest"
|
||||
BlueprintName = "xctests"
|
||||
ReferencedContainer = "container:Libnotify.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94099C722087E7D50004B6BC"
|
||||
BuildableName = "xctests.xctest"
|
||||
BlueprintName = "xctests"
|
||||
ReferencedContainer = "container:Libnotify.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
1098
libnotify.c
Normal file
1098
libnotify.c
Normal file
File diff suppressed because it is too large
Load Diff
226
libnotify.h
Normal file
226
libnotify.h
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2012 Apple Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#ifndef _LIBNOTIFY_H_
|
||||
#define _LIBNOTIFY_H_
|
||||
|
||||
#include <os/lock.h>
|
||||
#include <sys/queue.h>
|
||||
#include <pthread.h>
|
||||
#include <mach/mach.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
#include "table.h"
|
||||
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
extern const char *_notify_shm_id(void);
|
||||
#define SHM_ID _notify_shm_id()
|
||||
|
||||
#define NOTIFY_IPC_VERSION_NAME "com.apple.system.notify.ipc_version"
|
||||
#define NOTIFY_IPC_VERSION_NAME_LEN 35
|
||||
#define NOTIFY_SERVICE_NAME "com.apple.system.notification_center"
|
||||
#define NOTIFY_SERVICE_NAME_LEN 36
|
||||
#define NOTIFY_IPC_VERSION_MIN_SUPPORTED 3
|
||||
#define NOTIFY_IPC_VERSION 3
|
||||
|
||||
/* extra internal flags to notify_register_mach_port */
|
||||
/* Make sure this doesn't conflict with any flags in notify.h or notify_private.h */
|
||||
#define _NOTIFY_COMMON_PORT 0x40000000
|
||||
|
||||
/* Notification types */
|
||||
#define NOTIFY_TYPE_NONE 0x00000000
|
||||
#define NOTIFY_TYPE_MEMORY 0x00000001
|
||||
#define NOTIFY_TYPE_PLAIN 0x00000002
|
||||
#define NOTIFY_TYPE_PORT 0x00000004
|
||||
#define NOTIFY_TYPE_FILE 0x00000008
|
||||
#define NOTIFY_TYPE_SIGNAL 0x00000010
|
||||
#define NOTIFY_TYPE_MASK 0x0000001f // If this is changed, make sure it doesn't muck with struct client_s
|
||||
#define NOTIFY_FLAG_SELF 0x80000000
|
||||
#define NOTIFY_FLAG_REGEN 0x40000000
|
||||
#define NOTIFY_FLAG_RELEASE_SEND 0x20000000
|
||||
#define NOTIFY_FLAG_DISPATCH 0x10000000
|
||||
#define NOTIFY_TYPE_COALESCE_BASE 0x08000000
|
||||
#define NOTIFY_TYPE_COALESCED 0x04000000
|
||||
#define NOTIFY_FLAG_RETAINED 0x02000000
|
||||
#define NOTIFY_FLAG_CANCELED 0x01000000
|
||||
|
||||
/* Used to check for a polled or delivered types */
|
||||
#define NOTIFY_TYPE_POLLED (NOTIFY_TYPE_MEMORY | NOTIFY_TYPE_PLAIN)
|
||||
#define NOTIFY_TYPE_DELIVERED (NOTIFY_TYPE_PORT | NOTIFY_TYPE_FILE | NOTIFY_TYPE_SIGNAL)
|
||||
|
||||
/* Return values for notify_check() */
|
||||
#define NOTIFY_CHECK_FALSE 0
|
||||
#define NOTIFY_CHECK_TRUE 1
|
||||
#define NOTIFY_CHECK_ERROR 2
|
||||
|
||||
/* Access control */
|
||||
#define NOTIFY_ACCESS_READ 1
|
||||
#define NOTIFY_ACCESS_WRITE 2
|
||||
|
||||
#define NOTIFY_ACCESS_OTHER_SHIFT 8
|
||||
#define NOTIFY_ACCESS_GROUP_SHIFT 4
|
||||
#define NOTIFY_ACCESS_USER_SHIFT 0
|
||||
|
||||
#define NOTIFY_ACCESS_DEFAULT 0x00000333
|
||||
#define NOTIFY_ACCESS_USER_RW 0x00000003
|
||||
|
||||
/* Filesystem Services */
|
||||
#define NOTIFY_SERVICE_FILE_STATUS_QUO 0x00
|
||||
#define NOTIFY_SERVICE_FILE_ADD 0x01
|
||||
#define NOTIFY_SERVICE_FILE_DELETE 0x02
|
||||
#define NOTIFY_SERVICE_FILE_MODIFY 0x04
|
||||
#define NOTIFY_SERVICE_FILE_ATTR 0x08
|
||||
|
||||
#define NOTIFY_SERVICE_DIR_FILE_ADD 0x10
|
||||
#define NOTIFY_SERVICE_DIR_FILE_DELETE 0x20
|
||||
|
||||
#define NOTIFY_CLIENT_STATE_SUSPENDED 0x00000020
|
||||
#define NOTIFY_CLIENT_STATE_PENDING 0x00000040
|
||||
#define NOTIFY_CLIENT_STATE_TIMEOUT 0x00000080
|
||||
|
||||
/* port_data_t::flags and proc_data_t::flags */
|
||||
#define PORT_PROC_FLAGS_NONE 0x00000000
|
||||
#define NOTIFY_PORT_PROC_STATE_SUSPENDED 0x00000001
|
||||
|
||||
/* notify state flags */
|
||||
#define NOTIFY_STATE_USE_LOCKS 0x00000001
|
||||
#define NOTIFY_STATE_ENABLE_RESEND 0x00000002
|
||||
|
||||
#define NOTIFY_CLIENT_SELF 0
|
||||
#define SIGNAL_NONE -1
|
||||
#define FD_NONE -1
|
||||
#define SLOT_NONE -1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LIST_HEAD(, client_s) subscriptions;
|
||||
char *name;
|
||||
void *private;
|
||||
uint64_t name_id;
|
||||
uint64_t state;
|
||||
uint64_t state_time;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint32_t access;
|
||||
uint32_t slot;
|
||||
uint32_t refcount;
|
||||
uint32_t val;
|
||||
uint32_t postcount;
|
||||
uint32_t last_hour_postcount;
|
||||
} name_info_t;
|
||||
|
||||
typedef union client_delivery_u
|
||||
{
|
||||
int fd;
|
||||
mach_port_t port;
|
||||
uint32_t sig;
|
||||
} client_delivery_t;
|
||||
|
||||
typedef struct client_s
|
||||
{
|
||||
LIST_ENTRY(client_s) client_subscription_entry;
|
||||
LIST_ENTRY(client_s) client_pid_entry;
|
||||
LIST_ENTRY(client_s) client_port_entry;
|
||||
name_info_t *name_info;
|
||||
client_delivery_t deliver;
|
||||
union client_id {
|
||||
struct {
|
||||
uint32_t token;
|
||||
uint32_t pid;
|
||||
};
|
||||
uint64_t hash_key;
|
||||
} cid;
|
||||
uint32_t lastval;
|
||||
uint16_t service_index;
|
||||
uint8_t suspend_count;
|
||||
uint8_t state_and_type;
|
||||
} client_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LIST_HEAD(, client_s) clients;
|
||||
uint32_t port;
|
||||
uint32_t flags;
|
||||
} port_data_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LIST_HEAD(, client_s) clients;
|
||||
dispatch_source_t src;
|
||||
uint32_t pid;
|
||||
uint32_t flags;
|
||||
} proc_data_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* last allocated name id */
|
||||
uint64_t name_id;
|
||||
table_t name_table;
|
||||
table_64_t name_id_table;
|
||||
table_64_t client_table;
|
||||
table_n_t port_table;
|
||||
table_n_t proc_table;
|
||||
name_info_t **controlled_name;
|
||||
uint32_t flags;
|
||||
uint32_t controlled_name_count;
|
||||
os_unfair_lock lock;
|
||||
int sock;
|
||||
uint32_t stat_name_alloc;
|
||||
uint32_t stat_name_free;
|
||||
uint32_t stat_client_alloc;
|
||||
uint32_t stat_client_free;
|
||||
uint32_t stat_portproc_alloc;
|
||||
uint32_t stat_portproc_free;
|
||||
} notify_state_t;
|
||||
|
||||
void _notify_lib_notify_state_init(notify_state_t * ns, uint32_t flags);
|
||||
|
||||
uint32_t _notify_lib_post(notify_state_t *ns, const char *name, uint32_t uid, uint32_t gid);
|
||||
uint32_t _notify_lib_post_nid(notify_state_t *ns, uint64_t nid, uid_t uid, gid_t gid);
|
||||
uint32_t _notify_lib_post_client(notify_state_t *ns, client_t *c);
|
||||
|
||||
uint32_t _notify_lib_check(notify_state_t *ns, pid_t pid, int token, int *check);
|
||||
uint32_t _notify_lib_get_state(notify_state_t *ns, uint64_t nid, uint64_t *state, uint32_t uid, uint32_t gid);
|
||||
uint32_t _notify_lib_set_state(notify_state_t *ns, uint64_t nid, uint64_t state, uint32_t uid, uint32_t gid);
|
||||
|
||||
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);
|
||||
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_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);
|
||||
|
||||
void _notify_lib_resume_client(notify_state_t *ns, client_t *c, proc_data_t *proc_data, port_data_t *port_data);
|
||||
void _notify_lib_cancel_client(notify_state_t *ns, client_t *c);
|
||||
|
||||
void _notify_lib_cancel(notify_state_t *ns, pid_t pid, int token);
|
||||
uint32_t _notify_lib_suspend(notify_state_t *ns, pid_t pid, int token);
|
||||
uint32_t _notify_lib_resume(notify_state_t *ns, pid_t pid, int token);
|
||||
|
||||
uint32_t _notify_lib_check_controlled_access(notify_state_t *ns, char *name, uid_t uid, gid_t gid, int req);
|
||||
|
||||
uint64_t make_client_id(pid_t pid, int token);
|
||||
|
||||
|
||||
#endif /* _LIBNOTIFY_H_ */
|
472
notify.3
Normal file
472
notify.3
Normal file
@ -0,0 +1,472 @@
|
||||
.\" Copyright (c) 2003-2014 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
|
||||
.\" 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@
|
||||
.\"
|
||||
.\"
|
||||
.Dd September 3, 2008
|
||||
.Dt notify 3
|
||||
.Os "Mac OS X"
|
||||
.Sh NAME
|
||||
.Nm notify_post ,
|
||||
.Nm notify_register_check ,
|
||||
.Nm notify_register_dispatch ,
|
||||
.Nm notify_register_signal ,
|
||||
.Nm notify_register_mach_port ,
|
||||
.Nm notify_register_file_descriptor ,
|
||||
.Nm notify_check ,
|
||||
.Nm notify_get_state ,
|
||||
.Nm notify_set_state ,
|
||||
.Nm notify_suspend ,
|
||||
.Nm notify_resume ,
|
||||
.Nm notify_cancel ,
|
||||
.Nm notify_is_valid_token
|
||||
.Nd event distribution functions
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <notify.h>
|
||||
.Ft uint32_t
|
||||
.Fn notify_post "const char *name"
|
||||
.Ft uint32_t
|
||||
.Fn notify_register_check "const char *name, int *out_token"
|
||||
.Ft void
|
||||
.Fn (^notify_handler_t) "int token"
|
||||
.Ft uint32_t
|
||||
.Fn notify_register_dispatch "const char *name, int *out_token" "dispatch_queue_t queue" "notify_handler_t handler"
|
||||
.Ft uint32_t
|
||||
.Fn notify_register_signal "const char *name, int sig, int *out_token"
|
||||
.Ft uint32_t
|
||||
.Fn notify_register_mach_port "const char *name, mach_port_t *notify_port, int flags, int *out_token"
|
||||
.Ft uint32_t
|
||||
.Fn notify_register_file_descriptor "const char *name, int *notify_fd, int flags, int *out_token"
|
||||
.Ft uint32_t
|
||||
.Fn notify_check "int token, int *check"
|
||||
.Ft uint32_t
|
||||
.Fn notify_set_state "int token, uint64_t state"
|
||||
.Ft uint32_t
|
||||
.Fn notify_get_state "int token, uint64_t *state"
|
||||
.Ft uint32_t
|
||||
.Fn notify_suspend "int token"
|
||||
.Ft uint32_t
|
||||
.Fn notify_resume "int token"
|
||||
.Ft uint32_t
|
||||
.Fn notify_cancel "int token"
|
||||
.Ft bool
|
||||
.Fn notify_is_valid_token "int val"
|
||||
.Sh DESCRIPTION
|
||||
These routines allow processes to exchange stateless notification events.
|
||||
Processes post notifications to a single system-wide notification server,
|
||||
which then distributes notifications to client processes that have
|
||||
registered to receive those notifications, including processes run by
|
||||
other users.
|
||||
.Pp
|
||||
Notifications are associated with names in a namespace shared by all
|
||||
clients of the system.
|
||||
Clients may post notifications for names, and
|
||||
may monitor names for posted notifications.
|
||||
Clients may request
|
||||
notification delivery by a number of different methods.
|
||||
.Pp
|
||||
Clients desiring to monitor names in the notification system must
|
||||
register with the system, providing a name and other information
|
||||
required for the desired notification delivery method.
|
||||
Clients are
|
||||
given an integer token representing the registration.
|
||||
Token values are zero or positive integers.
|
||||
.Pp
|
||||
The kernel provides limited queues for mach message and file descriptor messages.
|
||||
It is important to make sure that clients read mach ports and file descriptors frequently
|
||||
to prevent messages from being lost due to resource limitations.
|
||||
Clients that use signal-based notification should be aware that signals
|
||||
are not delivered to a process while it is running in a signal handler.
|
||||
This may affect the delivery of signals in close succession.
|
||||
.Pp
|
||||
Notifications may be coalesced in some cases.
|
||||
Multiple events posted
|
||||
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 has been posted since a previous call to
|
||||
notify_check() for that name.
|
||||
.Pp
|
||||
"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.
|
||||
.Ss notify_post
|
||||
This routine causes the system to send a notification for the given
|
||||
name to all clients that have registered for notifications of this name.
|
||||
This is the only API required for an application that only produces
|
||||
notifications.
|
||||
.Ss notify_register_check
|
||||
Registers for passive notification for the given name.
|
||||
The routine generates
|
||||
a token that may be used with the
|
||||
.Fn notify_check
|
||||
routine to check if any notifications have been posted for the name.
|
||||
The check is implemented using a shared memory scheme, making the check
|
||||
very fast and efficient.
|
||||
The implementation has a limited amount
|
||||
of shared memory, so developers are encouraged to use this mechanism
|
||||
sparingly.
|
||||
It is also important to release the resources consumed
|
||||
by a registration with
|
||||
.Fn notify_cancel
|
||||
when they are no longer required by the application.
|
||||
.Ss notify_register_dispatch
|
||||
registers a callback handler in the form of a block which will be
|
||||
dispatched to the queue when a notification for the given name is
|
||||
received. This is a convenient way to register callbacks without any
|
||||
management of file descriptors, mach ports, or signals on the part of
|
||||
the application. The given queue is retained by the system for the
|
||||
lifetime of the notification. Use
|
||||
.Fn notify_cancel
|
||||
to release the notification and its reference to the queue.
|
||||
.Ss notify_register_signal
|
||||
registers a client for notification delivery via a signal.
|
||||
This fits
|
||||
well with the design of many UNIX daemons that use a signal such as SIGHUP
|
||||
to reinitialize of reset internal state information.
|
||||
.Ss notify_register_mach_port
|
||||
registers a client for notification delivery via mach messaging.
|
||||
Notifications are delivered by an empty message sent to a mach port.
|
||||
By default, a new port is created by a call to this routine.
|
||||
A mach port
|
||||
previously created 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 rights to the port.
|
||||
.Pp
|
||||
Values for the flags parameter may only be 0 (zero) or NOTIFY_REUSE.
|
||||
.Pp
|
||||
Note that the kernel limits the size of the message queue for any port.
|
||||
If it is important that notifications should not be lost due to queue
|
||||
overflow, clients should service messages quickly, and be cautious in
|
||||
using the same port for notifications for more than one name.
|
||||
.Pp
|
||||
A notification message has an empty message body.
|
||||
The msgh_id field
|
||||
in the mach message header will have the value of the notification
|
||||
token.
|
||||
If a port is reused for multiple notification registrations,
|
||||
the msgh_id value may be used to determine which name generated
|
||||
the notification.
|
||||
.Ss notify_register_file_descriptor
|
||||
Register for notification by a write to a file descriptor.
|
||||
.Pp
|
||||
By default, a new file descriptor is created and a pointer to it
|
||||
is returned as the value of the "notify_fd" parameter.
|
||||
A file descriptor
|
||||
created by a previous call to this routine may be used for notifications
|
||||
if a pointer to that file descriptor is passed in to the routine and
|
||||
NOTIFY_REUSE is set in the flags parameter.
|
||||
.Pp
|
||||
Values for the flags parameter may only be 0 (zero) or NOTIFY_REUSE.
|
||||
.Pp
|
||||
Note that the kernel limits the buffer space for queued writes on a
|
||||
file descriptor.
|
||||
If it is important that notifications should not be
|
||||
lost due to queue overflow, clients should service messages quickly,
|
||||
and be cautious in using the same file descriptor for notifications
|
||||
for more than one name.
|
||||
.Pp
|
||||
Notifications are delivered by an integer value written to the
|
||||
file descriptor.
|
||||
The value is sent in network byte order.
|
||||
When converted to host byte order, for example by using
|
||||
.Fn ntohl ,
|
||||
it will match the notification token
|
||||
for which the notification was generated.
|
||||
.Ss notify_check
|
||||
Checks if any notifications have been posted for a name.
|
||||
The output
|
||||
parameter "check" is set to 0 for false, 1 for true.
|
||||
A true indication is
|
||||
returned the first time notify_check is called for a token.
|
||||
Subsequent calls
|
||||
give a true indication when notifications have been posted for the name
|
||||
associated with the notification token.
|
||||
.Pp
|
||||
.Fn notify_check
|
||||
may be used with any notification token produced by any of the notification
|
||||
registration routines.
|
||||
A fast check based on a shared memory implementation
|
||||
is used when the token was generated by
|
||||
.Fn notify_register_check .
|
||||
Other tokens are checked by a call to the notification server.
|
||||
.Ss notify_set_state
|
||||
Set a 64-bit unsigned integer variable associated with a token.
|
||||
.Pp
|
||||
Each registered notification key has an associated 64-bit integer variable,
|
||||
which may be set using this routine and examined using the
|
||||
.Fn notify_get_state
|
||||
routine.
|
||||
The state variable is free to be used by clients of the notification API.
|
||||
It may be used to synchronize state information between cooperating processes or threads.
|
||||
(Available in Mac OS X 10.5 or later.)
|
||||
.Ss notify_get_state
|
||||
Get the 64-bit unsigned integer value associated with a token.
|
||||
The default value of a state variable is zero.
|
||||
(Available in Mac OS X 10.5 or later.)
|
||||
.Ss notify_suspend
|
||||
Suspends delivery of notifications for a notification token.
|
||||
Any notifications corresponding to a token that are posted while it is suspended
|
||||
will be coalesced, and pended until notifications are resumed using
|
||||
.Fn notify_resume .
|
||||
.Pp
|
||||
Calls to
|
||||
.Fn notify_suspend
|
||||
may be nested.
|
||||
Notifications will resume only when a matching number of calls are made to
|
||||
.Fn notify_resume .
|
||||
.Ss notify_resume
|
||||
Removes one level of suspension for a token previously suspended by a call to
|
||||
.Fn notify_suspend .
|
||||
When resumed, notifications will be delivered normally.
|
||||
A single notification will be generated if any notifications were pended while the token was suspended.
|
||||
.Ss notify_cancel
|
||||
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
|
||||
the port or file descriptor have been cancelled.
|
||||
.Pp
|
||||
.Ss notify_is_valid_token
|
||||
Determines if an integer value is valid for a current registration.
|
||||
Negative integers are never valid.
|
||||
A positive or zero value is valid if the current process has a registration associated with the given value.
|
||||
|
||||
.Sh RETURN VALUES
|
||||
Many notify functions return status (uint32_t) to indicate success or failure.
|
||||
This will always be one of the following:
|
||||
|
||||
.Ss NOTIFY_STATUS_OK
|
||||
The function did not encounter any issues.
|
||||
|
||||
.Ss NOTIFY_STATUS_INVALID_NAME
|
||||
Name argument is not valid.
|
||||
Often this will indicate that the name passed to the function is NULL.
|
||||
|
||||
.Ss NOTIFY_STATUS_INVALID_TOKEN
|
||||
The function expected a valid token, given by a notify_register_* function, and was passed an invalid token.
|
||||
Token validity can by checked with
|
||||
.Fn notify_is_valid_token .
|
||||
|
||||
.Ss NOTIFY_STATUS_INVALID_PORT
|
||||
The function is not able to use the port passed.
|
||||
This may be because the port is NULL, MACH_PORT_NULL, MACH_PORT_DEAD, or the calling process does not have the
|
||||
correct rights to the port.
|
||||
|
||||
.Ss NOTIFY_STATUS_INVALID_FILE
|
||||
The function was passed NULL, or something that is not a file descriptor generated by notify_register_file_descriptor.
|
||||
|
||||
.Ss NOTIFY_STATUS_INVALID_SIGNAL
|
||||
Legacy, currently unused.
|
||||
|
||||
.Ss NOTIFY_STATUS_INVALID_REQUEST
|
||||
An internal error occurred.
|
||||
|
||||
.Ss NOTIFY_STATUS_NOT_AUTHORIZED
|
||||
The calling process is not authorized to take this action.
|
||||
Usually this indicates that the calling process may not act on the name given.
|
||||
|
||||
.Ss NOTIFY_STATUS_OPT_DISABLE
|
||||
An internal error occurred.
|
||||
|
||||
.Ss NOTIFY_STATUS_SERVER_NOT_FOUND
|
||||
The server could not be found.
|
||||
This usually indicates that sandboxing is preventing the calling process from accessing notifyd.
|
||||
|
||||
.Ss NOTIFY_STATUS_NULL_INPUT
|
||||
One of the inputs was called with NULL when it must not be NULL.
|
||||
For legacy support, if name, token, port, or file descriptor is NULL, the respective NOTIFY_STATUS_INVALID_* return code
|
||||
will be used instead.
|
||||
|
||||
.Ss NOTIFY_STATUS_FAILED
|
||||
Indicates an internal failure of the library.
|
||||
The caller may try again; another attempt may be successful.
|
||||
Please report any instances where this is returned.
|
||||
|
||||
|
||||
|
||||
.Sh NAMESPACE CONVENTIONS
|
||||
Names in the namespace must be NULL-terminated.
|
||||
Names should be encoded as UTF-8 strings.
|
||||
.Pp
|
||||
The namespace supported by the system is unstructured, but users of
|
||||
this API are highly encouraged to follow the reverse-ICANN domain
|
||||
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.
|
||||
.Pp
|
||||
Names in the space "user.uid.UID", where UID is a numeric user ID number
|
||||
are reserved for processes with that UID.
|
||||
Names in this protected space may only be accessed or modified by processes
|
||||
with the effective UID specified as the UID in the name.
|
||||
The name "user.uid.UID" is protected for the given UID, as are any
|
||||
names of the form "user.uid.UID.<sub-path>".
|
||||
In the latter case, the name must have a dot character following the UID.
|
||||
.Pp
|
||||
Third party developers are encouraged to choose a prefix for names
|
||||
that will avoid conflicts in the shared namespace.
|
||||
.Pp
|
||||
The portion of the namespace prefixed by the string "self." is set aside
|
||||
for private use by applications.
|
||||
That is, each client may use that part
|
||||
of the namespace for intra-process notifications.
|
||||
These notifications
|
||||
are private to each individual process and are not propagated between
|
||||
processes.
|
||||
.Sh USAGE EXAMPLES
|
||||
A notification producer.
|
||||
.Pp
|
||||
#include <notify.h>
|
||||
...
|
||||
.Pp
|
||||
notify_post("com.eg.random.event");
|
||||
.Pp
|
||||
A client using notify_check() to determine when to invalidate a cache.
|
||||
.Pp
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <notify.h>
|
||||
.Pp
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
uint32_t status;
|
||||
int token, check;
|
||||
.Pp
|
||||
status = notify_register_check("com.eg.update", &token);
|
||||
if (status != NOTIFY_STATUS_OK)
|
||||
{
|
||||
fprintf(stderr, "registration failed (%u)\\n", status);
|
||||
exit(status);
|
||||
}
|
||||
.Pp
|
||||
build_my_cache();
|
||||
.Pp
|
||||
...
|
||||
.Pp
|
||||
status = notify_check(token, &check);
|
||||
if ((status == NOTIFY_STATUS_OK) && (check != 0))
|
||||
{
|
||||
/* An update has occurred - invalidate the cache */
|
||||
reset_my_cache();
|
||||
}
|
||||
.Pp
|
||||
...
|
||||
.Pp
|
||||
A client using file descriptor notifications.
|
||||
.Pp
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <notify.h>
|
||||
.Pp
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
uint32_t status;
|
||||
int nf, rtoken, qtoken, t, ret;
|
||||
fd_set readfds;
|
||||
.Pp
|
||||
status = notify_register_file_descriptor("com.eg.random.event",
|
||||
&nf, 0, &rtoken);
|
||||
if (status != NOTIFY_STATUS_OK)
|
||||
{
|
||||
fprintf(stderr, "registration failed (%u)\\n", status);
|
||||
exit(status);
|
||||
}
|
||||
.Pp
|
||||
status = notify_register_file_descriptor("com.eg.random.quit",
|
||||
&nf, NOTIFY_REUSE, &qtoken);
|
||||
if (status != NOTIFY_STATUS_OK)
|
||||
{
|
||||
fprintf(stderr, "registration failed (%u)\\n", status);
|
||||
exit(status);
|
||||
}
|
||||
.Pp
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(nf, &readfds);
|
||||
.Pp
|
||||
for (;;)
|
||||
{
|
||||
ret = select(nf+1, &readfds, NULL, NULL, NULL);
|
||||
if (ret <= 0) continue;
|
||||
if (!FD_ISSET(nf, &readfds)) continue;
|
||||
.Pp
|
||||
status = read(nf, &t, sizeof(int));
|
||||
if (status < 0)
|
||||
{
|
||||
perror("read");
|
||||
break;
|
||||
}
|
||||
.Pp
|
||||
t = ntohl(t);
|
||||
.Pp
|
||||
if (t == rtoken) printf("random event\\n");
|
||||
else if (t == qtoken) break;
|
||||
}
|
||||
.Pp
|
||||
printf("shutting down\\n");
|
||||
notify_cancel(rtoken);
|
||||
notify_cancel(qtoken);
|
||||
exit(0);
|
||||
}
|
||||
.Pp
|
||||
A client using dispatch notifications.
|
||||
.Pp
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <notify.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
.Pp
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
uint32_t status;
|
||||
int token;
|
||||
.Pp
|
||||
status = notify_register_dispatch("com.eg.random.event", &token,
|
||||
dispatch_get_main_queue(), ^(int t) {
|
||||
printf("com.eg.random.event received!\\n"); });
|
||||
.Pp
|
||||
dispatch_main();
|
||||
exit(0);
|
||||
}
|
||||
.Sh HISTORY
|
||||
These functions first appeared in
|
||||
Mac OS X 10.3.
|
||||
.Sh SEE ALSO
|
||||
.Xr ntohl 3 ,
|
||||
.Xr read 2 ,
|
||||
.Xr select 2 ,
|
||||
.Xr signal 3
|
344
notify.h
Normal file
344
notify.h
Normal file
@ -0,0 +1,344 @@
|
||||
/*
|
||||
* 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
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#ifndef __NOTIFICATION_H__
|
||||
#define __NOTIFICATION_H__
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <stdint.h>
|
||||
#include <mach/message.h>
|
||||
#include <os/base.h>
|
||||
#include <Availability.h>
|
||||
#ifdef __BLOCKS__
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif /* __BLOCKS__ */
|
||||
|
||||
/*! @header
|
||||
* These routines allow processes to exchange stateless notification events.
|
||||
* Processes post notifications to a single system-wide notification server,
|
||||
* which then distributes notifications to client processes that have
|
||||
* registered to receive those notifications, including processes run by
|
||||
* other users.
|
||||
*
|
||||
* Notifications are associated with names in a namespace shared by all
|
||||
* clients of the system. Clients may post notifications for names, and
|
||||
* may monitor names for posted notifications. Clients may request
|
||||
* notification delivery by a number of different methods.
|
||||
*
|
||||
* Clients desiring to monitor names in the notification system must
|
||||
* register with the system, providing a name and other information
|
||||
* required for the desired notification delivery method. Clients are
|
||||
* given an integer token representing the registration.
|
||||
*
|
||||
* Note that the kernel provides limited queues for mach message and file
|
||||
* descriptor messages. It is important to make sure that clients read
|
||||
* mach ports and file descriptors frequently to prevent messages from
|
||||
* being lost due to resource limitations. Clients that use signal-based
|
||||
* notification should be aware that signals are not delivered to
|
||||
* a process while it is running in a signal handler. This may affect
|
||||
* the delivery of signals in close succession.
|
||||
*
|
||||
* Notifications may be coalesced in some cases. Multiple events posted
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* Synchronization between two processes may be achieved using the
|
||||
* notify_set_state() and notify_get_state() routines.
|
||||
*/
|
||||
|
||||
/*! @defineblock Status Codes
|
||||
* Status codes returned by the API. See notify(3) for detailed description.
|
||||
*/
|
||||
#define NOTIFY_STATUS_OK 0
|
||||
#define NOTIFY_STATUS_INVALID_NAME 1
|
||||
#define NOTIFY_STATUS_INVALID_TOKEN 2
|
||||
#define NOTIFY_STATUS_INVALID_PORT 3
|
||||
#define NOTIFY_STATUS_INVALID_FILE 4
|
||||
#define NOTIFY_STATUS_INVALID_SIGNAL 5
|
||||
#define NOTIFY_STATUS_INVALID_REQUEST 6
|
||||
#define NOTIFY_STATUS_NOT_AUTHORIZED 7
|
||||
#define NOTIFY_STATUS_OPT_DISABLE 8
|
||||
#define NOTIFY_STATUS_SERVER_NOT_FOUND 9
|
||||
#define NOTIFY_STATUS_NULL_INPUT 10
|
||||
|
||||
#define NOTIFY_STATUS_FAILED 1000000
|
||||
|
||||
/*! @/defineblock */
|
||||
|
||||
/*!
|
||||
* Flag bits used for registration.
|
||||
*/
|
||||
#define NOTIFY_REUSE 0x00000001
|
||||
|
||||
|
||||
/*!
|
||||
* Token values are zero or positive integers.
|
||||
* NOTIFY_TOKEN_INVALID is useful as an initial value for
|
||||
* a token value passed as an in/out parameter to one of
|
||||
* the registration routines below.
|
||||
*/
|
||||
#define NOTIFY_TOKEN_INVALID -1
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/*!
|
||||
* Post a notification for a name.
|
||||
*
|
||||
* This is the only call that is required for a notification producer.
|
||||
* Returns status.
|
||||
*/
|
||||
OS_EXPORT uint32_t notify_post(const char *name);
|
||||
|
||||
|
||||
#ifdef __BLOCKS__
|
||||
typedef void (^notify_handler_t)(int token);
|
||||
|
||||
/*!
|
||||
* @function notify_register
|
||||
* @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
|
||||
* dispatch queue. Notification blocks are not re-entrant,
|
||||
* and subsequent notification Blocks will not be delivered
|
||||
* for the same registration until the previous Block has
|
||||
* returned.
|
||||
* @param name (input) The notification name.
|
||||
* @param out_token (output) The registration token.
|
||||
* @param queue (input) The dispatch queue to which the Block is submitted.
|
||||
* The dispatch queue is retained by the notify subsystem while
|
||||
* the notification is registered, and will be released when
|
||||
* notification is canceled.
|
||||
* @param handler (input) The Block to invoke on the dispatch queue in response
|
||||
* to a notification. The notification token is passed to the
|
||||
* Block as an argument so that the callee can modify the state
|
||||
* of the notification or cancel the registration.
|
||||
* @result Returns status.
|
||||
*/
|
||||
OS_EXPORT uint32_t notify_register_dispatch(const char *name, int *out_token, dispatch_queue_t queue, notify_handler_t handler)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_3_2);
|
||||
#endif /* __BLOCKS__ */
|
||||
|
||||
/*!
|
||||
* Creates a registration token be used with notify_check(),
|
||||
* but no active notifications will be delivered.
|
||||
*
|
||||
* @param name
|
||||
* (input) notification name
|
||||
* @param out_token
|
||||
* (output) registration token
|
||||
* @result Returns status.
|
||||
*/
|
||||
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
|
||||
* token to determine which name (if any) generated the signal notification.
|
||||
*
|
||||
* @param name (input) notification name
|
||||
* @param sig (input) signal number (see signal(3))
|
||||
* @param out_token (output) notification token
|
||||
* @result Returns status.
|
||||
*/
|
||||
OS_EXPORT uint32_t notify_register_signal(const char *name, int sig, int *out_token);
|
||||
|
||||
/*!
|
||||
* 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
|
||||
* 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
|
||||
* rights to the port.
|
||||
*
|
||||
* Note that the kernel limits the size of the message queue for any port.
|
||||
* If it is important that notifications should not be lost due to queue
|
||||
* overflow, clients should service messages quickly, and be careful about
|
||||
* using the same port for notifications for more than one name.
|
||||
*
|
||||
* A notification message has an empty message body. The msgh_id field
|
||||
* in the mach message header will have the value of the notification
|
||||
* token. If a port is reused for multiple notification registrations,
|
||||
* the msgh_id value may be used to determine which name generated
|
||||
* the notification.
|
||||
*
|
||||
* @param name
|
||||
* (input) notification name
|
||||
* @param out_token
|
||||
* (output) notification token
|
||||
* @param notify_port
|
||||
* (input/output) pointer to a mach port
|
||||
* @result Returns status.
|
||||
*/
|
||||
OS_EXPORT uint32_t notify_register_mach_port(const char *name, mach_port_t *notify_port, int flags, int *out_token);
|
||||
|
||||
/*!
|
||||
* Request notification by a write to a file descriptor.
|
||||
*
|
||||
* Notifications are delivered by a write to a file descriptor.
|
||||
* 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.
|
||||
*
|
||||
* Note that the kernel limits the buffer space for queued writes on a
|
||||
* file descriptor. If it is important that notifications should not be
|
||||
* lost due to queue overflow, clients should service messages quickly,
|
||||
* and be careful about using the same file descriptor for notifications
|
||||
* for more than one name.
|
||||
*
|
||||
* Notifications are delivered by an integer value written to the
|
||||
* file descriptor. The value will match the notification token
|
||||
* for which the notification was generated.
|
||||
*
|
||||
* @param name
|
||||
* (input) notification name
|
||||
* @param out_token
|
||||
* (output) notification token
|
||||
* @param notify_fd
|
||||
* (input/output) pointer to a file descriptor
|
||||
* @result Returns status.
|
||||
*/
|
||||
OS_EXPORT uint32_t notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int *out_token);
|
||||
|
||||
/*!
|
||||
* Check if any notifications have been posted.
|
||||
*
|
||||
* Output parameter check is set to 0 for false, 1 for true. Returns status.
|
||||
* check is set to true the first time notify_check is called for a token.
|
||||
* Subsequent calls set check to true when notifications have been posted for
|
||||
* the name associated with the notification token. This routine is independent
|
||||
* of notify_post(). That is, check will be true if an application calls
|
||||
* notify_post() for a name and then calls notify_check() for a token associated
|
||||
* with that name.
|
||||
*
|
||||
* @param token
|
||||
* (input)notification token
|
||||
* @param check
|
||||
* (output) true/false indication
|
||||
* @result Returns status.
|
||||
*/
|
||||
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
|
||||
* the port or file descriptor have been cancelled.
|
||||
*
|
||||
* @param token
|
||||
* (input) notification token
|
||||
* @result Returns status.
|
||||
*/
|
||||
OS_EXPORT uint32_t notify_cancel(int token);
|
||||
|
||||
/*!
|
||||
* Suspend delivery of notifications for a token. Notifications for this token will be
|
||||
* pended and coalesced, then delivered following a matching call to notify_resume.
|
||||
* Calls to notify_suspend may be nested. Notifications remain suspended until
|
||||
* an equal number of calls have been made to notify_resume.
|
||||
*
|
||||
* @param token
|
||||
* (input) notification token
|
||||
* @result Returns status.
|
||||
*/
|
||||
OS_EXPORT uint32_t notify_suspend(int token)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0);
|
||||
|
||||
/*!
|
||||
* Removes one level of suspension for a token previously suspended
|
||||
* by a call to notify_suspend. Notifications will resume when a matching
|
||||
* call to notify_resume is made for each previous call to notify_suspend.
|
||||
* Notifications posted while a token is suspended are coalesced into
|
||||
* a single notification sent following a resumption.
|
||||
*
|
||||
* @param token
|
||||
* (input) notification token
|
||||
* @result Returns status.
|
||||
*/
|
||||
OS_EXPORT uint32_t notify_resume(int token)
|
||||
__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
|
||||
* 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
|
||||
* not know whether the resource is available. The server can set the state value,
|
||||
* and the client can check the value at startup time to synchronize with the server.
|
||||
*
|
||||
* Set the 64-bit integer state value.
|
||||
*
|
||||
* @param token
|
||||
* (input) notification token
|
||||
* @param state64
|
||||
* (input) 64-bit unsigned integer value
|
||||
* @result Returns status.
|
||||
*/
|
||||
OS_EXPORT uint32_t notify_set_state(int token, uint64_t state64)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
|
||||
|
||||
/*!
|
||||
* Get the 64-bit integer state value.
|
||||
*
|
||||
* @param token
|
||||
* (input) notification token
|
||||
* @param state64
|
||||
* (output) 64-bit unsigned integer value
|
||||
* @result Returns status.
|
||||
*/
|
||||
OS_EXPORT uint32_t notify_get_state(int token, uint64_t *state64)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
|
||||
|
||||
/*!
|
||||
* Determine if a token is valid (currently registered).
|
||||
* Negative integer values are always invalid. Positive or
|
||||
* zero values are valid only if they are associated with an
|
||||
* existing registration.
|
||||
*
|
||||
* @param val
|
||||
* (input) integer value
|
||||
* @result Returns true if the value is a valid token, false otherwise.
|
||||
*/
|
||||
OS_EXPORT bool notify_is_valid_token(int val)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_10,__IPHONE_8_0);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* __NOTIFICATION_H__ */
|
1
notify_cancel.3
Normal file
1
notify_cancel.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/notify.3
|
1
notify_check.3
Normal file
1
notify_check.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/notify.3
|
4148
notify_client.c
Normal file
4148
notify_client.c
Normal file
File diff suppressed because it is too large
Load Diff
1
notify_get_state.3
Normal file
1
notify_get_state.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/notify.3
|
153
notify_internal.h
Normal file
153
notify_internal.h
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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@
|
||||
*/
|
||||
|
||||
#define __OS_EXPOSE_INTERNALS__ 1
|
||||
#include <os/internal/internal_shared.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_STATUS_SERVER_CHECKIN_FAILED 11
|
||||
// was NOTIFY_STATUS_LIB_SELF_STATE_FAILED 12
|
||||
#define NOTIFY_STATUS_SERVER_REGEN_FAILED 13
|
||||
#define NOTIFY_STATUS_CLIENT_REG_FAILED 14
|
||||
#define NOTIFY_STATUS_SERVER_POST_4_FAILED 15
|
||||
#define NOTIFY_STATUS_SERVER_POST_2_FAILED 16
|
||||
#define NOTIFY_STATUS_SERVER_POST_3_FAILED 17
|
||||
#define NOTIFY_STATUS_TOKEN_NOT_FOUND 18
|
||||
#define NOTIFY_STATUS_COMMON_PORT_NULL 19
|
||||
#define NOTIFY_STATUS_SERVER_PORT_NULL 20
|
||||
#define NOTIFY_STATUS_REG_CHECK_2_FAILED 21
|
||||
#define NOTIFY_STATUS_SHM_ATTACH_FAILED 22
|
||||
#define NOTIFY_STATUS_SHM_BASE_REMAINS_NULL 23
|
||||
#define NOTIFY_STATUS_REG_PLAIN_2_FAILED 24
|
||||
#define NOTIFY_STATUS_REG_SIGNAL_2_FAILED 25
|
||||
#define NOTIFY_STATUS_MACH_PORT_ALLOC_FAILED 26
|
||||
#define NOTIFY_STATUS_MACH_PORT_INSERT_RIGHT_FAILED 27
|
||||
#define NOTIFY_STATUS_REG_MACH_PORT_2_FAILED 28
|
||||
#define NOTIFY_STATUS_PIPE_FAILED 29
|
||||
#define NOTIFY_STATUS_FILEPORT_MAKEPORT_FAILED 30
|
||||
#define NOTIFY_STATUS_REG_FD_2_FAILED 31
|
||||
#define NOTIFY_STATUS_SHM_BASE_NULL 32
|
||||
#define NOTIFY_STATUS_SERVER_CHECK_FAILED 33
|
||||
#define NOTIFY_STATUS_STRDUP_FAILED 34
|
||||
#define NOTIFY_STATUS_SERVER_MONITOR_FILE_2_FAILED 35
|
||||
#define NOTIFY_STATUS_SERVER_GET_STATE_2_FAILED 36
|
||||
#define NOTIFY_STATUS_SERVER_SET_STATE_2_FAILED 37
|
||||
#define NOTIFY_STATUS_SERVER_SUSPEND_FAILED 38
|
||||
#define NOTIFY_STATUS_SERVER_RESUME_FAILED 39
|
||||
#define NOTIFY_STATUS_SERVER_SUSPEND_PID_FAILED 40
|
||||
#define NOTIFY_STATUS_SERVER_RESUME_PID_FAILED 41
|
||||
#define NOTIFY_STATUS_ALLOC_FAILED 42
|
||||
#define NOTIFY_STATUS_KILL_FAILED 43
|
||||
#define NOTIFY_STATUS_WRITE_FAILED 44
|
||||
#define NOTIFY_STATUS_MACH_MSG_TIMEOUT 45
|
||||
#define NOTIFY_STATUS_MACH_MSG_FAILED 46
|
||||
#define NOTIFY_STATUS_NEW_NAME_FAILED 47
|
||||
#define NOTIFY_STATUS_NEW_CLIENT_FAILED 48
|
||||
#define NOTIFY_STATUS_STATE_NULL 49
|
||||
#define NOTIFY_STATUS_CLIENT_NOT_FOUND 50
|
||||
#define NOTIFY_STATUS_DUP_CLIENT 51
|
||||
#define NOTIFY_STATUS_TYPE_ISSUE 52
|
||||
#define NOTIFY_STATUS_PATH_NODE_CREATE_FAILED 53
|
||||
#define NOTIFY_STATUS_INVALID_TIME_EVENT 54
|
||||
#define NOTIFY_STATUS_TIMER_FAILED 55
|
||||
#define NOTIFY_STATUS_DOUBLE_REG 56
|
||||
#define NOTIFY_STATUS_NO_REGEN_NEEDED 57
|
||||
|
||||
#define IS_INTERNAL_ERROR(X) (X >= 11)
|
||||
|
||||
#define USER_PROTECTED_UID_PREFIX "user.uid."
|
||||
#define USER_PROTECTED_UID_PREFIX_LEN 9
|
||||
|
||||
struct notify_globals_s
|
||||
{
|
||||
/* global lock */
|
||||
os_unfair_lock notify_lock;
|
||||
|
||||
/* notify_check() lock */
|
||||
os_unfair_lock check_lock;
|
||||
pid_t notify_server_pid;
|
||||
|
||||
atomic_uint_fast32_t client_opts;
|
||||
uint32_t saved_opts;
|
||||
|
||||
notify_state_t self_state;
|
||||
|
||||
dispatch_once_t notify_server_port_once;
|
||||
mach_port_t notify_server_port;
|
||||
mach_port_t saved_server_port;
|
||||
|
||||
mach_port_t notify_common_port;
|
||||
int notify_common_token;
|
||||
dispatch_source_t notify_dispatch_source;
|
||||
dispatch_source_t server_proc_source;
|
||||
|
||||
dispatch_once_t internal_once;
|
||||
table_n_t registration_table;
|
||||
table_t name_node_table;
|
||||
atomic_uint_fast32_t token_id;
|
||||
|
||||
dispatch_once_t make_background_send_queue_once;
|
||||
dispatch_queue_t background_send_queue;
|
||||
|
||||
/* file descriptor list */
|
||||
uint32_t fd_count;
|
||||
int *fd_clnt;
|
||||
int *fd_srv;
|
||||
int *fd_refcount;
|
||||
|
||||
/* mach port list */
|
||||
uint32_t mp_count;
|
||||
uint32_t mp_size;
|
||||
struct mp_entry {
|
||||
mach_port_t mpl_port_name;
|
||||
int mpl_refs;
|
||||
bool mpl_mine;
|
||||
} *mp_list;
|
||||
|
||||
/* shared memory base address */
|
||||
uint32_t *shm_base;
|
||||
};
|
||||
|
||||
typedef struct notify_globals_s *notify_globals_t;
|
||||
|
||||
__private_extern__ uint32_t _notify_lib_peek(notify_state_t *ns, pid_t pid, int token, int *val);
|
||||
|
261
notify_ipc.defs
Normal file
261
notify_ipc.defs
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#include <mach/std_types.defs>
|
||||
#include <mach/mach_types.defs>
|
||||
|
||||
subsystem notify_ipc 78945000;
|
||||
serverprefix _;
|
||||
|
||||
import <sys/types.h>;
|
||||
|
||||
type notify_name = c_string[*:512]
|
||||
ctype : caddr_t;
|
||||
|
||||
type notify_path = array[] of char
|
||||
ctype : caddr_t;
|
||||
|
||||
UseSpecialReplyPort 1;
|
||||
|
||||
skip; // was _notify_server_register_plain
|
||||
skip; // was _notify_server_cancel
|
||||
|
||||
routine _notify_server_check
|
||||
(
|
||||
server : mach_port_t;
|
||||
token : int;
|
||||
out check : int;
|
||||
out status : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
routine _notify_server_get_state
|
||||
(
|
||||
server : mach_port_t;
|
||||
token : int;
|
||||
out state : uint64_t;
|
||||
out status : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
|
||||
routine _notify_server_suspend
|
||||
(
|
||||
server : mach_port_t;
|
||||
token : int;
|
||||
out status : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
routine _notify_server_resume
|
||||
(
|
||||
server : mach_port_t;
|
||||
token : int;
|
||||
out status : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
simpleroutine _notify_server_suspend_pid
|
||||
(
|
||||
server : mach_port_t;
|
||||
pid : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
simpleroutine _notify_server_resume_pid
|
||||
(
|
||||
server : mach_port_t;
|
||||
pid : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
|
||||
|
||||
MsgOption MACH_SEND_PROPAGATE_QOS;
|
||||
|
||||
routine _notify_server_post_2
|
||||
(
|
||||
server : mach_port_t;
|
||||
name : notify_name;
|
||||
out name_id : uint64_t;
|
||||
out status : int;
|
||||
claim_root_access : boolean_t;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
simpleroutine _notify_server_post_3
|
||||
(
|
||||
server : mach_port_t;
|
||||
name_id : uint64_t;
|
||||
claim_root_access : boolean_t;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
simpleroutine _notify_server_post_4
|
||||
(
|
||||
server : mach_port_t;
|
||||
name : notify_name;
|
||||
claim_root_access : boolean_t;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
MsgOption MACH_MSG_OPTION_NONE;
|
||||
|
||||
simpleroutine _notify_server_register_plain_2
|
||||
(
|
||||
server : mach_port_t;
|
||||
name : notify_name;
|
||||
token : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
routine _notify_server_register_check_2
|
||||
(
|
||||
server : mach_port_t;
|
||||
name : notify_name;
|
||||
token: int;
|
||||
out size : int;
|
||||
out slot : int;
|
||||
out name_id : uint64_t;
|
||||
out status : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
simpleroutine _notify_server_register_signal_2
|
||||
(
|
||||
server : mach_port_t;
|
||||
name : notify_name;
|
||||
token : int;
|
||||
sig: int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
simpleroutine _notify_server_register_file_descriptor_2
|
||||
(
|
||||
server : mach_port_t;
|
||||
name : notify_name;
|
||||
token: int;
|
||||
fileport : mach_port_move_send_t;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
simpleroutine _notify_server_register_mach_port_2
|
||||
(
|
||||
server : mach_port_t;
|
||||
name : notify_name;
|
||||
token: int;
|
||||
port : mach_port_make_send_t;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
simpleroutine _notify_server_cancel_2
|
||||
(
|
||||
server : mach_port_t;
|
||||
token : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
routine _notify_server_get_state_2
|
||||
(
|
||||
server : mach_port_t;
|
||||
name_id : uint64_t;
|
||||
out state : uint64_t;
|
||||
out status : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
routine _notify_server_get_state_3
|
||||
(
|
||||
server : mach_port_t;
|
||||
token : int;
|
||||
out state : uint64_t;
|
||||
out nid : uint64_t;
|
||||
out status : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
simpleroutine _notify_server_set_state_2
|
||||
(
|
||||
server : mach_port_t;
|
||||
name_id : uint64_t;
|
||||
state : uint64_t;
|
||||
claim_root_access : boolean_t;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
routine _notify_server_set_state_3
|
||||
(
|
||||
server : mach_port_t;
|
||||
token : int;
|
||||
state : uint64_t;
|
||||
out nid : uint64_t;
|
||||
out status : int;
|
||||
claim_root_access : boolean_t;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
simpleroutine _notify_server_monitor_file_2
|
||||
(
|
||||
server : mach_port_t;
|
||||
token : int;
|
||||
path : notify_path;
|
||||
flags : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
routine _notify_server_regenerate
|
||||
(
|
||||
server : mach_port_t;
|
||||
name : notify_name;
|
||||
token : int;
|
||||
reg_type : uint32_t;
|
||||
port : mach_port_make_send_t;
|
||||
sig: int;
|
||||
prev_slot: int;
|
||||
prev_state : uint64_t;
|
||||
prev_time : uint64_t;
|
||||
path : notify_path;
|
||||
path_flags: int;
|
||||
out new_slot : int;
|
||||
out new_name_id : uint64_t;
|
||||
out status : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
|
||||
routine _notify_server_checkin
|
||||
(
|
||||
server : mach_port_t;
|
||||
out version: uint32_t;
|
||||
out server_pid : uint32_t;
|
||||
out status : int;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
||||
|
||||
|
||||
routine _notify_server_dump
|
||||
(
|
||||
server : mach_port_t;
|
||||
fileport : mach_port_move_send_t;
|
||||
ServerAuditToken audit : audit_token_t
|
||||
);
|
1
notify_is_valid_token.3
Normal file
1
notify_is_valid_token.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/notify.3
|
83
notify_keys.h
Normal file
83
notify_keys.h
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2007-2009 Apple Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* Portions Copyright (c) 2007-2009 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file lists notification keys that are posted using the
|
||||
* notify_post() API by various Mac OS X system services.
|
||||
* The exact circumstances under which services post these
|
||||
* notifications is controlled by those services, and may change
|
||||
* in future software releases.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Directory Service notifications
|
||||
* These are posted by the DirectoryService daemon to advise clients that
|
||||
* cached data should be invalidated.
|
||||
*/
|
||||
#define kNotifyDSCacheInvalidation "com.apple.system.DirectoryService.InvalidateCache"
|
||||
#define kNotifyDSCacheInvalidationGroup "com.apple.system.DirectoryService.InvalidateCache.group"
|
||||
#define kNotifyDSCacheInvalidationHost "com.apple.system.DirectoryService.InvalidateCache.host"
|
||||
#define kNotifyDSCacheInvalidationService "com.apple.system.DirectoryService.InvalidateCache.service"
|
||||
#define kNotifyDSCacheInvalidationUser "com.apple.system.DirectoryService.InvalidateCache.user"
|
||||
|
||||
/*
|
||||
* File System notifications
|
||||
* These advise clients of various filesystem events.
|
||||
*/
|
||||
#define kNotifyVFSMount "com.apple.system.kernel.mount"
|
||||
#define kNotifyVFSUnmount "com.apple.system.kernel.unmount"
|
||||
#define kNotifyVFSUpdate "com.apple.system.kernel.mountupdate"
|
||||
#define kNotifyVFSLowDiskSpace "com.apple.system.lowdiskspace"
|
||||
#define kNotifyVFSLowDiskSpaceRootFS "com.apple.system.lowdiskspace.system"
|
||||
#define kNotifyVFSLowDiskSpaceOtherFS "com.apple.system.lowdiskspace.user"
|
||||
|
||||
/*
|
||||
* System Configuration notifications
|
||||
* These advise clients of changes in the system configuration
|
||||
* managed by the system configuration server (configd).
|
||||
* Note that a much richer set of notifications are available to
|
||||
* clients using the SCDynamicStore API.
|
||||
*/
|
||||
#define kNotifySCHostNameChange "com.apple.system.hostname"
|
||||
#define kNotifySCNetworkChange "com.apple.system.config.network_change"
|
||||
|
||||
/*
|
||||
* ASL notifications
|
||||
* Sent by syslogd to advise clients that new log messages have been
|
||||
* added to the ASL database.
|
||||
*/
|
||||
#define kNotifyASLDBUpdate "com.apple.system.logger.message"
|
||||
|
||||
/*
|
||||
* Time Zone change notification
|
||||
* Sent by notifyd when the system's timezone changes.
|
||||
*/
|
||||
#define kNotifyTimeZoneChange "com.apple.system.timezone"
|
||||
|
||||
/*
|
||||
* System clock change notification
|
||||
* Sent when a process modifies the system clock using the settimeofday system call.
|
||||
*/
|
||||
#define kNotifyClockSet "com.apple.system.clock_set"
|
1
notify_post.3
Normal file
1
notify_post.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/notify.3
|
70
notify_private.h
Normal file
70
notify_private.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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@
|
||||
*/
|
||||
|
||||
#ifndef __NOTIFY_PRIVATE_H__
|
||||
#define __NOTIFY_PRIVATE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <os/base.h>
|
||||
#include <Availability.h>
|
||||
|
||||
#define NOTIFY_OPT_DISPATCH 0x00000001
|
||||
#define NOTIFY_OPT_REGEN 0x00000002
|
||||
#define NOTIFY_OPT_ENABLE 0x04000000
|
||||
#define NOTIFY_OPT_DISABLE 0x08000000
|
||||
|
||||
#define NOTIFY_NO_DISPATCH 0x80000000
|
||||
|
||||
#define ROOT_ENTITLEMENT_KEY "com.apple.notify.root_access"
|
||||
|
||||
OS_EXPORT uint32_t notify_suspend_pid(pid_t pid)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0);
|
||||
|
||||
OS_EXPORT uint32_t notify_resume_pid(pid_t pid)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0);
|
||||
|
||||
OS_EXPORT uint32_t notify_simple_post(const char *name)
|
||||
__API_DEPRECATED("No longer supported, use notify_post", macos(10.7, 10.15), ios(4.3, 13.0), watchos(1.0, 6.0), tvos(1.0, 13.0));
|
||||
|
||||
OS_EXPORT void notify_set_options(uint32_t opts)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_6_0);
|
||||
|
||||
OS_EXPORT void _notify_fork_child(void)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3);
|
||||
|
||||
OS_EXPORT uint32_t notify_peek(int token, uint32_t *val)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3);
|
||||
|
||||
OS_EXPORT uint32_t notify_monitor_file(int token, char *path, int flags)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3);
|
||||
|
||||
OS_EXPORT uint32_t notify_get_event(int token, int *ev, char *buf, int *len)
|
||||
__API_DEPRECATED("No longer supported", macos(10.7, 10.15), ios(4.3, 13.0), watchos(1.0, 6.0), tvos(1.0, 13.0));
|
||||
|
||||
OS_EXPORT uint32_t notify_register_plain(const char *name, int *out_token)
|
||||
__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3);
|
||||
|
||||
OS_EXPORT uint32_t notify_dump_status(const char *filepath);
|
||||
|
||||
#endif /* __NOTIFY_PRIVATE_H__ */
|
7
notify_probes.d
Normal file
7
notify_probes.d
Normal file
@ -0,0 +1,7 @@
|
||||
provider notify {
|
||||
probe register_mach_port(const char *name, mach_port_name_t notify_port, int flags, int token);
|
||||
probe post(const char *name);
|
||||
probe check(int token, int check);
|
||||
probe deliver_start(const char *name);
|
||||
probe deliver_end(const char *name);
|
||||
};
|
16
notify_probes.h
Normal file
16
notify_probes.h
Normal file
@ -0,0 +1,16 @@
|
||||
// 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)
|
1
notify_register_check.3
Normal file
1
notify_register_check.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/notify.3
|
1
notify_register_dispatch.3
Normal file
1
notify_register_dispatch.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/notify.3
|
1
notify_register_file_descriptor.3
Normal file
1
notify_register_file_descriptor.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/notify.3
|
1
notify_register_mach_port.3
Normal file
1
notify_register_mach_port.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/notify.3
|
1
notify_register_signal.3
Normal file
1
notify_register_signal.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/notify.3
|
1
notify_set_state.3
Normal file
1
notify_set_state.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/notify.3
|
451
notifybench/notify_bench.c
Normal file
451
notifybench/notify_bench.c
Normal file
@ -0,0 +1,451 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <notify.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "notify_private.h"
|
||||
|
||||
#ifdef NO_OP_TESTS
|
||||
extern uint32_t notify_no_op_str_sync(const char *name, size_t len);
|
||||
extern uint32_t notify_no_op_str_async(const char *name, size_t len);
|
||||
extern uint32_t notify_no_op_int_sync(int n);
|
||||
extern uint32_t notify_no_op_int_async(int n);
|
||||
#endif
|
||||
|
||||
#define MAX_CNT 100
|
||||
#define MAX_SPL 10000
|
||||
|
||||
#define DEFAULT_CNT 50
|
||||
#if defined(__arm__)
|
||||
#define DEFAULT_SPL 501
|
||||
#else
|
||||
#define DEFAULT_SPL 1001
|
||||
#endif
|
||||
|
||||
static uint32_t cnt = DEFAULT_CNT;
|
||||
static uint32_t spl = DEFAULT_SPL;
|
||||
|
||||
static long double time_round_to = (long double)200.0;
|
||||
|
||||
static long double loop_cost;
|
||||
static mach_timebase_info_data_t tbi;
|
||||
static uint64_t dmy[MAX_SPL], reg_plain[MAX_SPL], cancel_plain[MAX_SPL], reg_port[MAX_SPL], cancel_port[MAX_SPL];
|
||||
static uint64_t post_plain1[MAX_SPL], post_plain2[MAX_SPL], post_plain3[MAX_SPL];
|
||||
static uint64_t set_state1[MAX_SPL], set_state2[MAX_SPL], get_state[MAX_SPL];
|
||||
static uint64_t reg_check[MAX_SPL], cancel_check[MAX_SPL];
|
||||
static uint64_t check1[MAX_SPL], check2[MAX_SPL], check3[MAX_SPL], check4[MAX_SPL], check5[MAX_SPL];
|
||||
static uint64_t reg_disp1[MAX_SPL], reg_disp2[MAX_SPL], cancel_disp[MAX_SPL];
|
||||
|
||||
volatile static int dispatch_changer = 0;
|
||||
|
||||
#ifdef NO_OP_TESTS
|
||||
static uint64_t nss[MAX_SPL], nsa[MAX_SPL], nis[MAX_SPL], nia[MAX_SPL];
|
||||
#endif
|
||||
|
||||
static void __attribute__((noinline))
|
||||
print_result(uint64_t *s, const char *str)
|
||||
{
|
||||
unsigned j;
|
||||
uint64_t m;
|
||||
long double dd = 0, mm, sd = 0;
|
||||
|
||||
for (j = 0 ; j < spl; j++) {
|
||||
dd += (long double)s[j]/(long double)cnt;
|
||||
}
|
||||
dd = dd/(long double)spl;
|
||||
|
||||
for (j = 0 ; j < spl; j++) {
|
||||
long double tmp = (long double)s[j]/(long double)cnt - dd;
|
||||
sd += tmp * tmp;
|
||||
}
|
||||
sd = sqrtl(sd/(long double)spl);
|
||||
|
||||
qsort_b(s, spl, sizeof(uint64_t),
|
||||
^(const void *a, const void *b){
|
||||
const uint64_t l = *(const uint64_t*)a;
|
||||
const uint64_t r = *(const uint64_t*)b;
|
||||
return l == r ? 0 : (l < r ? -1 : 1);
|
||||
});
|
||||
m = s[spl/2];
|
||||
mm = (long double)m / (long double)cnt;
|
||||
|
||||
if (tbi.numer != tbi.denom) {
|
||||
dd *= tbi.numer;
|
||||
dd /= tbi.denom;
|
||||
mm *= tbi.numer;
|
||||
mm /= tbi.denom;
|
||||
sd *= tbi.numer;
|
||||
sd /= tbi.denom;
|
||||
}
|
||||
|
||||
if (str) {
|
||||
dd -= loop_cost;
|
||||
mm -= loop_cost;
|
||||
} else {
|
||||
loop_cost = mm;
|
||||
}
|
||||
|
||||
dd /= NSEC_PER_USEC;
|
||||
dd = roundl(dd * time_round_to) / time_round_to;
|
||||
mm /= NSEC_PER_USEC;
|
||||
mm = roundl(mm * time_round_to) / time_round_to;
|
||||
sd /= NSEC_PER_USEC;
|
||||
sd = roundl(sd * time_round_to) / time_round_to;
|
||||
|
||||
if (!str) {
|
||||
printf("%-40s %-8s %-8s %-8s\n",
|
||||
"Symbol", "Median", "Average", "StdDev");
|
||||
}
|
||||
printf("%-36s%8.3Lf us %8.3Lf us %8.3Lf us\n",
|
||||
str ? str : "Empty loop:", mm, dd, sd);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_fence()
|
||||
{
|
||||
int fence_token;
|
||||
notify_register_check("com.apple.notify.test", &fence_token);
|
||||
notify_cancel(fence_token);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
uint32_t r;
|
||||
kern_return_t kr;
|
||||
unsigned i, j;
|
||||
|
||||
kr = mach_timebase_info(&tbi);
|
||||
assert(!kr);
|
||||
|
||||
int tok;
|
||||
r = notify_register_check("dummy.test", &tok);
|
||||
assert(r == 0);
|
||||
r = notify_cancel(tok);
|
||||
assert(r == 0);
|
||||
|
||||
int t[MAX_CNT];
|
||||
int t_2[MAX_CNT];
|
||||
mach_port_t p[MAX_CNT];
|
||||
char *n[MAX_CNT];
|
||||
size_t l[MAX_CNT];
|
||||
uint64_t s;
|
||||
int check;
|
||||
|
||||
volatile uint32_t spin = 0;
|
||||
|
||||
dispatch_queue_t disp_q = dispatch_queue_create("Notify.Test", NULL);
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if (!strcmp(argv[i], "-c")) cnt = atoi(argv[++i]);
|
||||
else if (!strcmp(argv[i], "-s")) spl = atoi(argv[++i]) + 1;
|
||||
}
|
||||
|
||||
if (cnt > MAX_CNT) cnt = MAX_CNT;
|
||||
if (spl > MAX_SPL) spl = MAX_SPL + 1;
|
||||
|
||||
for (j = 0 ; j < spl; j++)
|
||||
{
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = asprintf(&n[i], "dummy.test.%d", i);
|
||||
assert(r != -1);
|
||||
l[i] = strlen(n[i]);
|
||||
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &p[i]);
|
||||
assert(kr == 0);
|
||||
}
|
||||
|
||||
/* Empty Loop */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
spin++;
|
||||
}
|
||||
dmy[j] = mach_absolute_time() - s;
|
||||
|
||||
#ifdef NO_OP_TESTS
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_no_op_str_sync(n[i], l[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
nss[j] = mach_absolute_time() - s;
|
||||
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_no_op_str_async(n[i], l[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
nsa[j] = mach_absolute_time() - s;
|
||||
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_no_op_int_sync(i);
|
||||
assert(r == 0);
|
||||
}
|
||||
nis[j] = mach_absolute_time() - s;
|
||||
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_no_op_int_async(i);
|
||||
assert(r == 0);
|
||||
}
|
||||
nia[j] = mach_absolute_time() - s;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Register Plain */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_register_plain(n[i], &t[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
reg_plain[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Post 1 */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_post(n[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
post_plain1[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Post 2 */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_post(n[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
post_plain2[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Post 3 */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_post(n[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
post_plain3[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Cancel Plain */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_cancel(t[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
cancel_plain[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Register Mach Port */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_register_mach_port(n[i], &p[i], NOTIFY_REUSE, &t[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
reg_port[j] = mach_absolute_time() - s;
|
||||
|
||||
|
||||
/* Set State 1 */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_set_state(t[i], 1);
|
||||
assert(r == 0);
|
||||
}
|
||||
set_state1[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Get State */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
uint64_t dummy;
|
||||
r = notify_get_state(t[i], &dummy);
|
||||
assert(r == 0);
|
||||
}
|
||||
get_state[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Set State 2 */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_set_state(t[i], 2);
|
||||
assert(r == 0);
|
||||
}
|
||||
set_state2[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Cancel Port */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_cancel(t[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
cancel_port[j] = mach_absolute_time() - s;
|
||||
|
||||
|
||||
|
||||
/* Register Check */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_register_check("com.apple.notify.test.check", &t[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
reg_check[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Check 1 */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_check(t[i], &check);
|
||||
assert(r == 0);
|
||||
assert(check == 1);
|
||||
}
|
||||
check1[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Check 2 */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_check(t[i], &check);
|
||||
assert(r == 0);
|
||||
assert(check == 0);
|
||||
}
|
||||
check2[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Check 3 */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_check(t[i], &check);
|
||||
assert(r == 0);
|
||||
assert(check == 0);
|
||||
}
|
||||
check3[j] = mach_absolute_time() - s;
|
||||
|
||||
notify_post("com.apple.notify.test.check");
|
||||
|
||||
notify_fence();
|
||||
|
||||
/* Check 4 */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_check(t[i], &check);
|
||||
assert(r == 0);
|
||||
assert(check == 1);
|
||||
}
|
||||
check4[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Check 5 */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_check(t[i], &check);
|
||||
assert(r == 0);
|
||||
assert(check == 0);
|
||||
}
|
||||
check5[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Cancel Check */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_cancel(t[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
cancel_check[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Register Dispatch 1 */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_register_dispatch(n[i], &t[i], disp_q, ^(int x){
|
||||
dispatch_changer = x;
|
||||
});
|
||||
assert(r == 0);
|
||||
}
|
||||
reg_disp1[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Register Dispatch 2 (Coalesced) */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_register_dispatch(n[i], &t_2[i], disp_q, ^(int x){
|
||||
dispatch_changer = x;
|
||||
|
||||
});
|
||||
assert(r == 0);
|
||||
}
|
||||
reg_disp2[j] = mach_absolute_time() - s;
|
||||
|
||||
/* Cancel Dispatch */
|
||||
s = mach_absolute_time();
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
r = notify_cancel(t[i]);
|
||||
assert(r == 0);
|
||||
r = notify_cancel(t_2[i]);
|
||||
assert(r == 0);
|
||||
}
|
||||
cancel_disp[j] = mach_absolute_time() - s;
|
||||
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
free(n[i]);
|
||||
kr = mach_port_mod_refs(mach_task_self(), p[i], MACH_PORT_RIGHT_RECEIVE, -1);
|
||||
assert(kr == 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
print_result(dmy, NULL);
|
||||
#ifdef NO_OP_TESTS
|
||||
print_result(nss, "notify_no_op_str_sync:");
|
||||
print_result(nsa, "notify_no_op_str_async:");
|
||||
print_result(nis, "notify_no_op_int_sync:");
|
||||
print_result(nia, "notify_no_op_int_async:");
|
||||
#endif
|
||||
|
||||
|
||||
print_result(reg_plain, "notify_register_plain:");
|
||||
print_result(post_plain1, "notify_post [plain 1]:");
|
||||
print_result(post_plain2, "notify_post [plain 2]:");
|
||||
print_result(post_plain3, "notify_post [plain 3]:");
|
||||
print_result(cancel_plain, "notify_cancel [plain]:");
|
||||
print_result(reg_port, "notify_register_mach_port:");
|
||||
print_result(set_state1, "notify_set_state [1]:");
|
||||
print_result(set_state2, "notify_set_state [2]:");
|
||||
print_result(get_state, "notify_get_state:");
|
||||
print_result(cancel_port, "notify_cancel [port]");
|
||||
|
||||
print_result(reg_check, "notify_register_check:");
|
||||
print_result(check1, "notify_check [1]:");
|
||||
print_result(check2, "notify_check [2]:");
|
||||
print_result(check3, "notify_check [3]:");
|
||||
print_result(check4, "notify_check [4]:");
|
||||
print_result(check5, "notify_check [5]:");
|
||||
print_result(cancel_check, "notfiy_cancel [check]:");
|
||||
|
||||
print_result(reg_disp1, "notify_register_dispatch [1]:");
|
||||
print_result(reg_disp2, "notify_register_dispatch [2]:");
|
||||
print_result(cancel_disp, "notify_cancel [both disp]:");
|
||||
|
||||
return 0;
|
||||
}
|
30
notifyd/CMakeLists.txt
Normal file
30
notifyd/CMakeLists.txt
Normal file
@ -0,0 +1,30 @@
|
||||
project(notifyd)
|
||||
|
||||
add_compile_definitions(
|
||||
SINGLE_THREADED_NOTIFY_STATE=1
|
||||
MACH_NOTIFY_SEND_POSSIBLE_EXPECTED=1
|
||||
)
|
||||
|
||||
mig(notify_ipc.defs)
|
||||
mig(notify.defs)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(notifyd_sources
|
||||
notifyd.c
|
||||
notify_proc.c
|
||||
pathwatch.c
|
||||
service.c
|
||||
timer.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/notify_ipcServer.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/notifyServer.c
|
||||
)
|
||||
|
||||
add_darling_executable(notifyd ${notifyd_sources})
|
||||
target_link_libraries(notifyd system bsm.0)
|
||||
|
||||
install(TARGETS notifyd DESTINATION libexec/darling/usr/sbin)
|
||||
install(FILES com.apple.notifyd.plist DESTINATION libexec/darling/System/Library/LaunchDaemons)
|
||||
install(FILES notifyd.8 DESTINATION libexec/darling/usr/share/man/man8)
|
||||
install(FILES notify.conf.MacOSX RENAME notify.conf DESTINATION libexec/darling/etc)
|
||||
|
33
notifyd/com.apple.notifyd.plist
Normal file
33
notifyd/com.apple.notifyd.plist
Normal file
@ -0,0 +1,33 @@
|
||||
<?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>EnablePressuredExit</key>
|
||||
<false/>
|
||||
<key>EnableTransactions</key>
|
||||
<true/>
|
||||
<key>Label</key>
|
||||
<string>com.apple.notifyd</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>ASL_DISABLE</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
<key>MachServices</key>
|
||||
<dict>
|
||||
<key>com.apple.system.notification_center</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<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>
|
||||
</plist>
|
24
notifyd/com.apple.notifyd.sb
Normal file
24
notifyd/com.apple.notifyd.sb
Normal file
@ -0,0 +1,24 @@
|
||||
;; Copyright (c) 2015 Apple Inc. All Rights reserved.
|
||||
;;
|
||||
;; WARNING: The sandbox rules in this file currently constitute
|
||||
;; Apple System Private Interface and are subject to change at any time and
|
||||
;; without notice.
|
||||
;;
|
||||
|
||||
(version 1)
|
||||
(deny default)
|
||||
(import "system.sb")
|
||||
|
||||
;; Allow files to be read
|
||||
(allow file-read*)
|
||||
|
||||
;; Allow debug status files to be written
|
||||
(allow file-write*
|
||||
(regex #"^/private/var/run/notifyd")
|
||||
)
|
||||
|
||||
;; Allow UNIX signals
|
||||
(allow signal)
|
||||
|
||||
;; Allow shared memory
|
||||
(allow ipc-posix-shm)
|
11
notifyd/notify.conf.MacOSX
Normal file
11
notifyd/notify.conf.MacOSX
Normal file
@ -0,0 +1,11 @@
|
||||
#
|
||||
# Notification Center configuration file
|
||||
#
|
||||
|
||||
reserve com.apple.system. 0 0 rwr-r-
|
||||
reserve com.apple.system.clock_set 0 266 rwrwr-
|
||||
monitor com.apple.system.timezone /etc/localtime
|
||||
monitor com.apple.system.info:/etc/hosts /etc/hosts
|
||||
monitor com.apple.system.info:/etc/services /etc/services
|
||||
monitor com.apple.system.info:/etc/protocols /etc/protocols
|
||||
|
5
notifyd/notify.conf.iOSSimulator
Normal file
5
notifyd/notify.conf.iOSSimulator
Normal file
@ -0,0 +1,5 @@
|
||||
#
|
||||
# Notification Center configuration file
|
||||
#
|
||||
|
||||
monitor com.apple.system.timezone /etc/localtime
|
9
notifyd/notify.conf.iPhone
Normal file
9
notifyd/notify.conf.iPhone
Normal file
@ -0,0 +1,9 @@
|
||||
#
|
||||
# Notification Center configuration file
|
||||
#
|
||||
|
||||
reserve com.apple.system. 0 0 rwr-r-
|
||||
reserve com.apple.system.clock_set 0 266 rwrwr-
|
||||
monitor com.apple.system.timezone /var/db/timezone/localtime
|
||||
set com.apple.system.batterysavermode
|
||||
set com.apple.system.batterysavermode.discretionary
|
1
notifyd/notify.defs
Symbolic link
1
notifyd/notify.defs
Symbolic link
@ -0,0 +1 @@
|
||||
../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/notify.defs
|
1
notifyd/notify_ipc.defs
Symbolic link
1
notifyd/notify_ipc.defs
Symbolic link
@ -0,0 +1 @@
|
||||
../notify_ipc.defs
|
1350
notifyd/notify_proc.c
Normal file
1350
notifyd/notify_proc.c
Normal file
File diff suppressed because it is too large
Load Diff
68
notifyd/notifyd.8
Normal file
68
notifyd/notifyd.8
Normal file
@ -0,0 +1,68 @@
|
||||
.\" Copyright (c) 2003-2013 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
|
||||
.\" 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@
|
||||
.\"
|
||||
.\"
|
||||
.Dd March 24, 2003
|
||||
.Dt notifyd 8
|
||||
.Os "Mac OS X"
|
||||
.Sh NAME
|
||||
.Nm notifyd
|
||||
.Nd notification server
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl d
|
||||
.Op Fl log_file Ar path
|
||||
.Op Fl shm_pages Ar npages
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is the server for the Mac OS X notification system described in
|
||||
.Xr notify 3 .
|
||||
The server is started automatically by
|
||||
.Nm launchd
|
||||
during system startup.
|
||||
.Pp
|
||||
The
|
||||
.Fl d
|
||||
option causes
|
||||
.Nm notifyd
|
||||
to log debugging messages to a log file.
|
||||
Messages are not logged to ASL to avoid potential deadlocks,
|
||||
since the ASL system makes use of the
|
||||
.Xr notify 3
|
||||
system.
|
||||
.Pp
|
||||
The default log file is
|
||||
.Pa /var/log/notifyd.log .
|
||||
An alternate log file path may be specified following the
|
||||
.Fl log_file
|
||||
flag.
|
||||
.Pp
|
||||
The
|
||||
.Fl shm_pages Ar npages
|
||||
option sets the number of shared memory pages used for passive notification.
|
||||
The default is one page.
|
||||
If a value of zero is specified,
|
||||
shared memory is disabled and passive notifications are performed
|
||||
using IPC between the client and the server.
|
||||
.Sh SEE ALSO
|
||||
.Xr notify 3 .
|
1436
notifyd/notifyd.c
Normal file
1436
notifyd/notifyd.c
Normal file
File diff suppressed because it is too large
Load Diff
119
notifyd/notifyd.h
Normal file
119
notifyd/notifyd.h
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2003-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@
|
||||
*/
|
||||
|
||||
#ifndef _NOTIFY_DAEMON_H_
|
||||
#define _NOTIFY_DAEMON_H_
|
||||
|
||||
#define DISPATCH_MACH_SPI 1
|
||||
|
||||
#include <libnotify.h>
|
||||
#include <mach/mach.h>
|
||||
#include <launch.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <dispatch/private.h>
|
||||
|
||||
|
||||
#define STATUS_REQUEST_SHORT 0
|
||||
#define STATUS_REQUEST_LONG 1
|
||||
|
||||
#define NOTIFY_STATE_ENTITLEMENT "com.apple.private.libnotify.statecapture"
|
||||
|
||||
|
||||
struct global_s
|
||||
{
|
||||
notify_state_t notify_state;
|
||||
dispatch_mach_t mach_notifs_channel;
|
||||
mach_port_t mach_notify_port;
|
||||
mach_port_t server_port;
|
||||
void **service_info_list;
|
||||
dispatch_workloop_t workloop;
|
||||
dispatch_mach_t mach_channel;
|
||||
dispatch_source_t sig_usr1_src;
|
||||
dispatch_source_t sig_usr2_src;
|
||||
dispatch_source_t sig_winch_src;
|
||||
dispatch_source_t stat_reset_src;
|
||||
time_t last_reset_time;
|
||||
uint32_t nslots;
|
||||
uint32_t slot_id;
|
||||
uint32_t *shared_memory_base;
|
||||
uint32_t *shared_memory_refcount;
|
||||
uint32_t *last_shm_base;
|
||||
uint32_t log_cutoff;
|
||||
uint32_t log_default;
|
||||
uint16_t service_info_count;
|
||||
char *log_path;
|
||||
};
|
||||
|
||||
extern struct global_s global;
|
||||
|
||||
struct call_statistics_s
|
||||
{
|
||||
uint64_t post;
|
||||
uint64_t post_no_op;
|
||||
uint64_t post_by_id;
|
||||
uint64_t post_by_name;
|
||||
uint64_t post_by_name_and_fetch_id;
|
||||
uint64_t reg;
|
||||
uint64_t reg_plain;
|
||||
uint64_t reg_check;
|
||||
uint64_t reg_signal;
|
||||
uint64_t reg_file;
|
||||
uint64_t reg_port;
|
||||
uint64_t cancel;
|
||||
uint64_t suspend;
|
||||
uint64_t resume;
|
||||
uint64_t suspend_pid;
|
||||
uint64_t resume_pid;
|
||||
uint64_t check;
|
||||
uint64_t get_state;
|
||||
uint64_t get_state_by_client;
|
||||
uint64_t get_state_by_id;
|
||||
uint64_t get_state_by_client_and_fetch_id;
|
||||
uint64_t set_state;
|
||||
uint64_t set_state_by_client;
|
||||
uint64_t set_state_by_id;
|
||||
uint64_t set_state_by_client_and_fetch_id;
|
||||
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;
|
||||
uint64_t checkin;
|
||||
};
|
||||
|
||||
extern struct call_statistics_s call_statistics;
|
||||
|
||||
extern void log_message(int priority, const char *str, ...) __printflike(2, 3);
|
||||
extern uint32_t daemon_post(const char *name, uint32_t u, uint32_t g);
|
||||
extern uint32_t daemon_post_nid(uint64_t nid, uint32_t u, uint32_t g);
|
||||
extern void daemon_post_client(uint64_t cid);
|
||||
extern void daemon_set_state(const char *name, uint64_t val);
|
||||
extern void dump_status(uint32_t level, int fd);
|
||||
extern bool has_entitlement(audit_token_t audit, const char *entitlement);
|
||||
extern bool has_root_entitlement(audit_token_t audit);
|
||||
|
||||
dispatch_queue_t get_notifyd_workloop(void);
|
||||
|
||||
#endif /* _NOTIFY_DAEMON_H_ */
|
947
notifyd/pathwatch.c
Normal file
947
notifyd/pathwatch.c
Normal file
@ -0,0 +1,947 @@
|
||||
/*
|
||||
* 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@
|
||||
*/
|
||||
|
||||
/*
|
||||
* These routines, accessed through path_node_create() and path_node_release(),
|
||||
* provide an API for monitoring a path in the filesystem. The path may contain
|
||||
* directories and symbolic links. The path may not even exist! If the path
|
||||
* does exist, this code will respond to path deletions, component renaming, and
|
||||
* access control changes that either delete the path or make it inaccessible to a
|
||||
* target user/group. A notification will be provided if the path comes back into
|
||||
* existance or again becomes accessible.
|
||||
*
|
||||
* path_node_create() returns a path_node_t object, which contains a dispatch_source_t.
|
||||
* This source behaves very much like a DISPATCH_SOURCE_TYPE_VNODE, except that it also
|
||||
* triggers on the creation of a path.
|
||||
*
|
||||
* Internally, the work of monitoring a path is done by a set of helper vnode_t
|
||||
* objects. A vnode_t contains a dispatch_source_t (of type DISPATCH_SOURCE_TYPE_VNODE)
|
||||
* for a particular vnode. When a path_node_t is created, it creates (or shares)
|
||||
* vnode_t objects for each component of the desired path. For example, a path_node_t
|
||||
* for "/a/b/c" will create (or share, if some other path_node_t has already created) a
|
||||
* dispatch_source_t for "/", "/a", "/a/b", and "/a/b/c". If any of these sources is
|
||||
* notified of a change, the vnode_t will trigger an update for all path_node_t
|
||||
* objects that contain that path component.
|
||||
*
|
||||
* When a path_node_t update is triggered by a vnode_t component, the node re-evaluates
|
||||
* the target path that it is charged with monitoring. If the path exists and the end-point
|
||||
* vnode changed, then the update operation will trigger its dispatch_source_t to notify the
|
||||
* end-user of the change. If an intermediate path component is removed, renamed, or becomes
|
||||
* blocked by an access-control change, then the end-point dispatch_source_t is triggered to
|
||||
* indicate that the path has been deleted. However, the path_node_t remains active and
|
||||
* monitors the path components that still exist. Eventually, if the path is recreated or
|
||||
* if access controls change so that the path becomes visible to the target user, then the
|
||||
* end-point dispatch_source_t is triggered with a PATH_NODE_CREATE bit set in its data flags.
|
||||
*
|
||||
* path_node_releases() releases a path_node_t object and all of the vnode_t objects
|
||||
* that were monitoring components of its target path.
|
||||
*
|
||||
* All of the code in this file is to be run on the workloop in order to maintain internal
|
||||
* datastructures. This is asserted in every non-static function and thus can be safely
|
||||
* assumed by all static functions.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/kauth.h>
|
||||
#include <pwd.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <tzfile.h>
|
||||
#include <sandbox.h>
|
||||
#include <bsm/libbsm.h>
|
||||
#include "pathwatch.h"
|
||||
#include "notifyd.h"
|
||||
|
||||
#define forever for(;;)
|
||||
#define streq(A,B) (strcmp(A,B)==0)
|
||||
#define DISPATCH_VNODE_ALL 0x7f
|
||||
|
||||
#define PATH_STAT_OK 0
|
||||
#define PATH_STAT_FAILED 1
|
||||
#define PATH_STAT_ACCESS 2
|
||||
|
||||
#define VPATH_NODE_TYPE_REG 0
|
||||
#define VPATH_NODE_TYPE_LINK 1
|
||||
#define VPATH_NODE_TYPE_DELETED 2
|
||||
|
||||
#define DISPATCH_VNODE_UNAVAIL (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE)
|
||||
|
||||
/* Libinfo global */
|
||||
extern uint32_t gL1CacheEnabled;
|
||||
|
||||
/*
|
||||
* vnode_t represents a vnode.
|
||||
*
|
||||
* The dispatch source is of type DISPATCH_SOURCE_TYPE_VNODE for file descriptor fd.
|
||||
* The handler for the source triggers an update routine for all the path_node_t
|
||||
* objects in the path_node list.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *path;
|
||||
uint32_t type;
|
||||
int fd;
|
||||
struct timespec mtime;
|
||||
struct timespec ctime;
|
||||
dispatch_source_t src;
|
||||
uint32_t path_node_count;
|
||||
path_node_t **path_node;
|
||||
} vnode_t;
|
||||
|
||||
static struct
|
||||
{
|
||||
dispatch_once_t pathwatch_init;
|
||||
uint32_t vnode_count;
|
||||
vnode_t **vnode;
|
||||
char *tzdir;
|
||||
size_t tzdir_len;
|
||||
} _global = {0};
|
||||
|
||||
/* forward */
|
||||
static void _path_node_update(path_node_t *pnode, uint32_t flags, vnode_t *vnode);
|
||||
|
||||
/*
|
||||
* stat() or lstat() a path as a particular user/group.
|
||||
*/
|
||||
static int
|
||||
_path_stat(const char *path, int link, uid_t uid, gid_t gid)
|
||||
{
|
||||
struct stat sb;
|
||||
gid_t orig_gidset[NGROUPS_MAX];
|
||||
int ngroups, status, stat_status;
|
||||
struct passwd *p;
|
||||
uint32_t orig_cache_enabled;
|
||||
|
||||
/* disable L1 cache to avoid notification deadlock */
|
||||
orig_cache_enabled = gL1CacheEnabled;
|
||||
gL1CacheEnabled = 0;
|
||||
|
||||
/* get my group list */
|
||||
memset(orig_gidset, 0, sizeof(orig_gidset));
|
||||
ngroups = getgroups(NGROUPS_MAX, orig_gidset);
|
||||
if (ngroups < 0)
|
||||
{
|
||||
return PATH_STAT_FAILED;
|
||||
}
|
||||
|
||||
/* look up user name */
|
||||
p = getpwuid(uid);
|
||||
if (p == NULL)
|
||||
{
|
||||
gL1CacheEnabled = orig_cache_enabled;
|
||||
return PATH_STAT_FAILED;
|
||||
}
|
||||
|
||||
/* switch to user's grouplist */
|
||||
status = initgroups(p->pw_name, gid);
|
||||
if (status < 0)
|
||||
{
|
||||
gL1CacheEnabled = orig_cache_enabled;
|
||||
return PATH_STAT_FAILED;
|
||||
}
|
||||
|
||||
/* reset gL1CacheEnabled */
|
||||
gL1CacheEnabled = orig_cache_enabled;
|
||||
|
||||
/* set thread credentials */
|
||||
pthread_setugid_np(uid, gid);
|
||||
|
||||
/* stat the file */
|
||||
stat_status = -1;
|
||||
if (link != 0)
|
||||
{
|
||||
stat_status = lstat(path, &sb);
|
||||
}
|
||||
else
|
||||
{
|
||||
stat_status = stat(path, &sb);
|
||||
}
|
||||
|
||||
/* unset thread credentials */
|
||||
pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE);
|
||||
|
||||
/* restore original grouplist for UID 0 */
|
||||
status = syscall(SYS_initgroups, ngroups, orig_gidset, 0);
|
||||
if (status < 0)
|
||||
{
|
||||
return PATH_STAT_FAILED;
|
||||
}
|
||||
|
||||
/* return status */
|
||||
if (stat_status == 0)
|
||||
{
|
||||
return PATH_STAT_OK;
|
||||
}
|
||||
|
||||
if (errno == EACCES)
|
||||
{
|
||||
return PATH_STAT_ACCESS;
|
||||
}
|
||||
|
||||
return PATH_STAT_FAILED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check access to a path by a particular user/group.
|
||||
* Sets ftype output parameter if it is non-NULL.
|
||||
*/
|
||||
static int
|
||||
_path_stat_check_access(const char *path, audit_token_t audit, bool client_is_notifyd, uint32_t *ftype)
|
||||
{
|
||||
struct stat sb;
|
||||
char buf[MAXPATHLEN + 1];
|
||||
int status, t;
|
||||
|
||||
if (path == NULL) return PATH_STAT_FAILED;
|
||||
|
||||
if (ftype != NULL) *ftype = PATH_NODE_TYPE_GHOST;
|
||||
|
||||
/* Paths must be absolute */
|
||||
if (path[0] != '/') return PATH_STAT_FAILED;
|
||||
|
||||
/* Root dir is readable */
|
||||
if (path[1] == '\0')
|
||||
{
|
||||
if (ftype != NULL) *ftype = PATH_NODE_TYPE_DIR;
|
||||
return PATH_STAT_OK;
|
||||
}
|
||||
|
||||
/* Don't perform stat if sandbox won't allow it. (15907527) */
|
||||
if (!client_is_notifyd && (sandbox_check_by_audit_token(audit, "file-read-metadata", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, path) != 0)) {
|
||||
return PATH_STAT_ACCESS;
|
||||
}
|
||||
|
||||
memset(&sb, 0, sizeof(struct stat));
|
||||
status = lstat(path, &sb);
|
||||
|
||||
if (status != 0) return PATH_STAT_FAILED;
|
||||
else if ((sb.st_mode & S_IFMT) == S_IFDIR) t = PATH_NODE_TYPE_DIR;
|
||||
else if ((sb.st_mode & S_IFMT) == S_IFREG) t = PATH_NODE_TYPE_FILE;
|
||||
else if ((sb.st_mode & S_IFMT) == S_IFLNK) t = PATH_NODE_TYPE_LINK;
|
||||
else t = PATH_NODE_TYPE_OTHER;
|
||||
|
||||
if (ftype != NULL) *ftype = t;
|
||||
|
||||
if (t == PATH_NODE_TYPE_OTHER) return PATH_STAT_FAILED;
|
||||
|
||||
/* skip access control check if uid is zero or if the client is notifyd */
|
||||
if (client_is_notifyd || audit_token_to_euid(audit) == 0) return 0;
|
||||
|
||||
/* special case: anything in the timezone directory is OK */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (realpath(path, buf) == NULL) return PATH_STAT_FAILED;
|
||||
if ((_global.tzdir != NULL) && (!strncasecmp(buf, _global.tzdir, _global.tzdir_len)))
|
||||
{
|
||||
return PATH_STAT_OK;
|
||||
}
|
||||
|
||||
/* call _path_stat to check access as the user/group provided */
|
||||
if (t == PATH_NODE_TYPE_FILE)
|
||||
{
|
||||
status = _path_stat(path, 0, audit_token_to_euid(audit), audit_token_to_egid(audit));
|
||||
if ((status == PATH_STAT_ACCESS) && (ftype != NULL)) *ftype = PATH_NODE_TYPE_GHOST;
|
||||
return status;
|
||||
}
|
||||
else if (t == PATH_NODE_TYPE_LINK)
|
||||
{
|
||||
status = _path_stat(path, 1, audit_token_to_euid(audit), audit_token_to_egid(audit));
|
||||
if ((status == PATH_STAT_ACCESS) && (ftype != NULL)) *ftype = PATH_NODE_TYPE_GHOST;
|
||||
return status;
|
||||
}
|
||||
else if (t == PATH_NODE_TYPE_DIR)
|
||||
{
|
||||
snprintf(buf, MAXPATHLEN, "%s/.", path);
|
||||
status = _path_stat(buf, 0, audit_token_to_euid(audit), audit_token_to_egid(audit));
|
||||
if ((status == PATH_STAT_ACCESS) && (ftype != NULL)) *ftype = PATH_NODE_TYPE_GHOST;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* we don't ever get here, but... */
|
||||
return PATH_STAT_FAILED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Uniquely add a pnode to a vnode's list of path nodes.
|
||||
*/
|
||||
static void
|
||||
_vnode_add_pnode(vnode_t *vnode, path_node_t *pnode)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < vnode->path_node_count; i++)
|
||||
{
|
||||
if (vnode->path_node[i] == pnode) return;
|
||||
}
|
||||
|
||||
for (i = 0; i < vnode->path_node_count; i++)
|
||||
{
|
||||
if (vnode->path_node[i] == NULL)
|
||||
{
|
||||
vnode->path_node[i] = pnode;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (vnode->path_node_count == 0)
|
||||
{
|
||||
vnode->path_node = (path_node_t **)calloc(1, sizeof(path_node_t *));
|
||||
}
|
||||
else
|
||||
{
|
||||
vnode->path_node = (path_node_t **)reallocf(vnode->path_node, (vnode->path_node_count + 1) * sizeof(path_node_t *));
|
||||
}
|
||||
|
||||
assert(vnode->path_node != NULL);
|
||||
|
||||
vnode->path_node[vnode->path_node_count++] = pnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free a vnode_t and cancel/release its dispatch source.
|
||||
*/
|
||||
static void
|
||||
_vnode_free(vnode_t *vnode)
|
||||
{
|
||||
dispatch_source_cancel(vnode->src);
|
||||
|
||||
dispatch_async(get_notifyd_workloop(), ^{
|
||||
dispatch_release(vnode->src);
|
||||
free(vnode->path);
|
||||
free(vnode->path_node);
|
||||
free(vnode);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Handler routine for vnode_t objects.
|
||||
* Invokes the _path_node_update routine for all of the vnode's pnodes.
|
||||
*/
|
||||
static void
|
||||
_vnode_event(vnode_t *vnode)
|
||||
{
|
||||
uint32_t i, flags;
|
||||
unsigned long ulf;
|
||||
struct stat sb;
|
||||
|
||||
if (vnode == NULL) return;
|
||||
if ((vnode->src != NULL) && (dispatch_source_testcancel(vnode->src))) return;
|
||||
|
||||
ulf = dispatch_source_get_data(vnode->src);
|
||||
flags = (uint32_t)ulf;
|
||||
|
||||
memset(&sb, 0, sizeof(struct stat));
|
||||
if (fstat(vnode->fd, &sb) == 0)
|
||||
{
|
||||
if ((vnode->mtime.tv_sec != sb.st_mtimespec.tv_sec) || (vnode->mtime.tv_nsec != sb.st_mtimespec.tv_nsec))
|
||||
{
|
||||
flags |= PATH_NODE_MTIME;
|
||||
vnode->mtime = sb.st_mtimespec;
|
||||
}
|
||||
|
||||
if ((vnode->ctime.tv_sec != sb.st_ctimespec.tv_sec) || (vnode->ctime.tv_nsec != sb.st_ctimespec.tv_nsec))
|
||||
{
|
||||
flags |= PATH_NODE_CTIME;
|
||||
vnode->ctime = sb.st_ctimespec;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Flag deleted sources.
|
||||
* We can't delete them here, since _path_node_update may need them.
|
||||
* However, _path_node_update will release them and they will get cleaned
|
||||
* up in a _vnode_sweep later on.
|
||||
*/
|
||||
if (flags & DISPATCH_VNODE_DELETE) vnode->type = VPATH_NODE_TYPE_DELETED;
|
||||
|
||||
for (i = 0; i < vnode->path_node_count; i++)
|
||||
{
|
||||
_path_node_update(vnode->path_node[i], flags, vnode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a vnode_t object.
|
||||
*/
|
||||
static vnode_t *
|
||||
_vnode_create(const char *path, uint32_t type, path_node_t *pnode)
|
||||
{
|
||||
int fd, flags;
|
||||
uint32_t i;
|
||||
vnode_t *vnode;
|
||||
dispatch_source_t src;
|
||||
struct stat sb;
|
||||
|
||||
if (path == NULL) path = "/";
|
||||
if (path[0] == '\0') path = "/";
|
||||
|
||||
for (i = 0; i < _global.vnode_count; i++)
|
||||
{
|
||||
vnode = _global.vnode[i];
|
||||
if (vnode == NULL) continue;
|
||||
|
||||
if ((vnode->type == type) && (streq(path, vnode->path)))
|
||||
{
|
||||
_vnode_add_pnode(vnode, pnode);
|
||||
return vnode;
|
||||
}
|
||||
}
|
||||
|
||||
vnode = NULL;
|
||||
|
||||
flags = O_EVTONLY;
|
||||
if (type == VPATH_NODE_TYPE_LINK) flags |= O_SYMLINK;
|
||||
|
||||
fd = open(path, flags, 0);
|
||||
if (fd < 0) return NULL;
|
||||
|
||||
src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, (uintptr_t)fd, DISPATCH_VNODE_ALL, get_notifyd_workloop());
|
||||
if (src == NULL)
|
||||
{
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vnode = (vnode_t *)calloc(1, sizeof(vnode_t));
|
||||
assert(vnode != NULL);
|
||||
|
||||
vnode->type = type;
|
||||
vnode->path = strdup(path);
|
||||
assert(vnode->path != NULL);
|
||||
|
||||
vnode->fd = fd;
|
||||
vnode->src = src;
|
||||
|
||||
memset(&sb, 0, sizeof(struct stat));
|
||||
if (fstat(fd, &sb) == 0)
|
||||
{
|
||||
vnode->mtime = sb.st_mtimespec;
|
||||
vnode->ctime = sb.st_ctimespec;
|
||||
}
|
||||
|
||||
_vnode_add_pnode(vnode, pnode);
|
||||
|
||||
dispatch_source_set_event_handler(src, ^{ _vnode_event(vnode); });
|
||||
dispatch_source_set_cancel_handler(src, ^{ close(fd); });
|
||||
|
||||
if (_global.vnode_count == 0)
|
||||
{
|
||||
_global.vnode = (vnode_t **)calloc(1, sizeof(vnode_t *));
|
||||
}
|
||||
else
|
||||
{
|
||||
_global.vnode = (vnode_t **)reallocf(_global.vnode, (_global.vnode_count + 1) * sizeof(vnode_t *));
|
||||
}
|
||||
|
||||
assert(_global.vnode != NULL);
|
||||
|
||||
_global.vnode[_global.vnode_count++] = vnode;
|
||||
|
||||
dispatch_resume(src);
|
||||
|
||||
return vnode;
|
||||
}
|
||||
|
||||
static vnode_t *
|
||||
_vnode_create_real_path(const char *path, uint32_t type, path_node_t *pnode)
|
||||
{
|
||||
char real[MAXPATHLEN + 1];
|
||||
|
||||
if (path == NULL) return _vnode_create(path, type, pnode);
|
||||
|
||||
if (NULL != realpath(path, real)) return _vnode_create(real, type, pnode);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Examines all the vnode_t objects (held in the _global data),
|
||||
* frees any that have no path nodes.
|
||||
*/
|
||||
static void
|
||||
_vnode_sweep()
|
||||
{
|
||||
uint32_t i, j, new_vnode_count, new_path_node_count;
|
||||
vnode_t **new_source, *vnode;
|
||||
path_node_t **new_path_node;
|
||||
|
||||
new_source = NULL;
|
||||
|
||||
for (i = 0; i < _global.vnode_count; i++)
|
||||
{
|
||||
vnode = _global.vnode[i];
|
||||
if (vnode == NULL) continue;
|
||||
|
||||
new_path_node_count = 0;
|
||||
new_path_node = NULL;
|
||||
|
||||
for (j = 0; j < vnode->path_node_count; j++)
|
||||
{
|
||||
if (vnode->path_node[j] != NULL) new_path_node_count++;
|
||||
}
|
||||
|
||||
if (new_path_node_count == vnode->path_node_count)
|
||||
{
|
||||
/* no change */
|
||||
continue;
|
||||
}
|
||||
else if (new_path_node_count > 0)
|
||||
{
|
||||
new_path_node = (path_node_t **)calloc(new_path_node_count, sizeof(path_node_t *));
|
||||
assert(new_path_node != NULL);
|
||||
|
||||
new_path_node_count = 0;
|
||||
for (j = 0; j < vnode->path_node_count; j++)
|
||||
{
|
||||
if (vnode->path_node[j] != NULL)
|
||||
{
|
||||
new_path_node[new_path_node_count++] = vnode->path_node[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(vnode->path_node);
|
||||
vnode->path_node = new_path_node;
|
||||
vnode->path_node_count = new_path_node_count;
|
||||
}
|
||||
|
||||
new_vnode_count = 0;
|
||||
for (i = 0; i < _global.vnode_count; i++)
|
||||
{
|
||||
vnode = _global.vnode[i];
|
||||
if (vnode == NULL) continue;
|
||||
if (vnode->path_node_count > 0) new_vnode_count++;
|
||||
}
|
||||
|
||||
if (new_vnode_count == _global.vnode_count)
|
||||
{
|
||||
/* no change */
|
||||
return;
|
||||
}
|
||||
else if (new_vnode_count > 0)
|
||||
{
|
||||
new_source = (vnode_t **)calloc(new_vnode_count, sizeof(vnode_t *));
|
||||
assert(new_source != NULL);
|
||||
|
||||
new_vnode_count = 0;
|
||||
for (i = 0; i < _global.vnode_count; i++)
|
||||
{
|
||||
vnode = _global.vnode[i];
|
||||
if (vnode == NULL) continue;
|
||||
|
||||
if (vnode->path_node_count > 0)
|
||||
{
|
||||
new_source[new_vnode_count++] = vnode;
|
||||
}
|
||||
else
|
||||
{
|
||||
_vnode_free(vnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(_global.vnode);
|
||||
_global.vnode = new_source;
|
||||
_global.vnode_count = new_vnode_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Releases sources that have a particular node on their list.
|
||||
* This is a deferred release mechanism for vnode_t objects.
|
||||
* The calling routine must call _vnode_sweep subsequent to
|
||||
* calling this routine.
|
||||
* _vnode_sweep will actually free any vnode_t objects
|
||||
* that have a no path nodes.
|
||||
*/
|
||||
static void
|
||||
_vnode_release_for_node(path_node_t *pnode)
|
||||
{
|
||||
uint32_t i, j;
|
||||
vnode_t *vnode;
|
||||
|
||||
for (i = 0; i < _global.vnode_count; i++)
|
||||
{
|
||||
vnode = _global.vnode[i];
|
||||
if (vnode == NULL) continue;
|
||||
|
||||
for (j = 0; j < vnode->path_node_count; j++)
|
||||
{
|
||||
if (vnode->path_node[j] == pnode)
|
||||
{
|
||||
vnode->path_node[j] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Retain a path_node_t object.
|
||||
*/
|
||||
static void
|
||||
_path_node_retain(path_node_t *pnode)
|
||||
{
|
||||
if (pnode == NULL) return;
|
||||
pnode->refcount++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free a path_node_t object.
|
||||
*/
|
||||
static void
|
||||
_path_node_free(path_node_t *pnode)
|
||||
{
|
||||
uint32_t i, n;
|
||||
|
||||
if (pnode == NULL) return;
|
||||
|
||||
/*
|
||||
* Remove this path node from all vnodes.
|
||||
*/
|
||||
_vnode_release_for_node(pnode);
|
||||
_vnode_sweep();
|
||||
|
||||
free(pnode->path);
|
||||
|
||||
if (pnode->pname != NULL)
|
||||
{
|
||||
n = pnode->pname_count;
|
||||
pnode->pname_count = 0;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
free(pnode->pname[i]);
|
||||
pnode->pname[i] = NULL;
|
||||
}
|
||||
|
||||
free(pnode->pname);
|
||||
}
|
||||
|
||||
free(pnode->contextp);
|
||||
|
||||
dispatch_release(pnode->src);
|
||||
|
||||
memset(pnode, 0, sizeof(path_node_t));
|
||||
free(pnode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release a path_node_t object.
|
||||
*/
|
||||
static void
|
||||
_path_node_release(path_node_t *pnode)
|
||||
{
|
||||
if (pnode == NULL) return;
|
||||
|
||||
|
||||
if (pnode->refcount > 0) pnode->refcount--;
|
||||
if (pnode->refcount == 0) _path_node_free(pnode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees a path_node_t object.
|
||||
*/
|
||||
void
|
||||
path_node_close(path_node_t *pnode)
|
||||
{
|
||||
dispatch_assert_queue(get_notifyd_workloop());
|
||||
|
||||
if (pnode == NULL) return;
|
||||
|
||||
if (pnode->src != NULL) dispatch_source_cancel(pnode->src);
|
||||
_path_node_release(pnode);
|
||||
}
|
||||
|
||||
static void
|
||||
_pathwatch_init()
|
||||
{
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
_global.tzdir = NULL;
|
||||
_global.tzdir_len = 0;
|
||||
|
||||
/* Get the real path to TZDIR */
|
||||
if (realpath(TZDIR, buf) != NULL)
|
||||
{
|
||||
_global.tzdir_len = strlen(buf);
|
||||
_global.tzdir = strdup(buf);
|
||||
if (_global.tzdir == NULL) _global.tzdir_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* _path_node_init is responsible for allocating a path_node_t structure,
|
||||
* and for creating the pname array and setting the path component.
|
||||
* The path is a sanatized version of the caller's path with redundant "/"
|
||||
* characters stripped out. The pname array contains each "/" separated
|
||||
* component of the path.
|
||||
*
|
||||
* For example, _path_node_init("///foo////bar//baz/") creates:
|
||||
* pnode->path = "/foo/bar/baz"
|
||||
* pnode->pname_count = 3
|
||||
* pnode->pname[0] = "foo"
|
||||
* pnode->pname[1] = "bar"
|
||||
* pnode->pname[2] = "baz"
|
||||
*/
|
||||
static path_node_t *
|
||||
_path_node_init(const char *path)
|
||||
{
|
||||
size_t len;
|
||||
uint32_t i;
|
||||
path_node_t *pnode;
|
||||
const char *start, *end;
|
||||
char *name;
|
||||
|
||||
if (path == NULL) path = "/";
|
||||
if (path[0] != '/') return NULL;
|
||||
|
||||
pnode = (path_node_t *)calloc(1, sizeof(path_node_t));
|
||||
assert(pnode != NULL);
|
||||
|
||||
pnode->plen = 1;
|
||||
start = path;
|
||||
while (*start == '/') start++;
|
||||
|
||||
forever
|
||||
{
|
||||
end = strchr(start, '/');
|
||||
if (end == NULL) end = strchr(start, '\0');
|
||||
|
||||
len = end - start;
|
||||
if (len == 0) break;
|
||||
|
||||
pnode->plen += (len + 1);
|
||||
|
||||
name = NULL;
|
||||
if (end == NULL)
|
||||
{
|
||||
name = strdup(start);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = malloc(len + 1);
|
||||
assert(name != NULL);
|
||||
strncpy(name, start, len);
|
||||
name[len] = '\0';
|
||||
}
|
||||
|
||||
if (pnode->pname_count == 0)
|
||||
{
|
||||
pnode->pname = (char **)calloc(1, sizeof(char *));
|
||||
}
|
||||
else
|
||||
{
|
||||
pnode->pname = (char **)reallocf(pnode->pname, (pnode->pname_count + 1) * sizeof(char *));
|
||||
}
|
||||
|
||||
assert(pnode->pname != NULL);
|
||||
pnode->pname[pnode->pname_count] = name;
|
||||
pnode->pname_count++;
|
||||
|
||||
start = end;
|
||||
if (start != NULL)
|
||||
{
|
||||
/* skip '/' chars */
|
||||
while (*start == '/') start++;
|
||||
}
|
||||
}
|
||||
|
||||
pnode->path = calloc(1, pnode->plen);
|
||||
assert(pnode->path != NULL);
|
||||
/*
|
||||
* Reconstruct the path here to strip out excess "/" chars.
|
||||
* This ensures that path comparisons in _path_node_update are correct.
|
||||
*/
|
||||
for (i = 0; i < pnode->pname_count; i++)
|
||||
{
|
||||
strlcat(pnode->path, "/", pnode->plen);
|
||||
strlcat(pnode->path, pnode->pname[i], pnode->plen);
|
||||
}
|
||||
|
||||
return pnode;
|
||||
}
|
||||
|
||||
static void
|
||||
_path_node_update(path_node_t *pnode, uint32_t flags, vnode_t *vnode)
|
||||
{
|
||||
char *buf, fixed[MAXPATHLEN + 1];
|
||||
uint32_t i, old_type;
|
||||
int status;
|
||||
unsigned long data;
|
||||
struct stat sb;
|
||||
|
||||
if (pnode == NULL) return;
|
||||
if ((pnode->src != NULL) && (dispatch_source_testcancel(pnode->src))) return;
|
||||
|
||||
old_type = pnode->type;
|
||||
|
||||
status = _path_stat_check_access(pnode->path, pnode->audit, pnode->flags & PATH_NODE_CLIENT_NOTIFYD, &(pnode->type));
|
||||
if (status == PATH_STAT_ACCESS) flags |= DISPATCH_VNODE_REVOKE;
|
||||
|
||||
data = 0;
|
||||
|
||||
if (vnode != NULL)
|
||||
{
|
||||
/* update status */
|
||||
|
||||
if (flags & DISPATCH_VNODE_UNAVAIL)
|
||||
{
|
||||
pnode->type = PATH_NODE_TYPE_GHOST;
|
||||
data |= (flags & DISPATCH_VNODE_UNAVAIL);
|
||||
data |= DISPATCH_VNODE_DELETE;
|
||||
}
|
||||
|
||||
if ((vnode->path != NULL) && (pnode->path != NULL) && streq(vnode->path, pnode->path))
|
||||
{
|
||||
/* this is the target VNODE - transfer flags to src data */
|
||||
data |= flags;
|
||||
}
|
||||
|
||||
if (old_type == PATH_NODE_TYPE_GHOST)
|
||||
{
|
||||
/* transition from ghost to non-ghost */
|
||||
if (pnode->type != PATH_NODE_TYPE_GHOST)
|
||||
{
|
||||
data |= PATH_NODE_CREATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = 0;
|
||||
}
|
||||
}
|
||||
else if (pnode->type == PATH_NODE_TYPE_GHOST)
|
||||
{
|
||||
/* transition from non-ghost to ghost */
|
||||
data |= PATH_NODE_DELETE;
|
||||
}
|
||||
|
||||
data &= (pnode->flags & PATH_NODE_ALL);
|
||||
if (data != 0)
|
||||
{
|
||||
if ((pnode->flags & PATH_SRC_SUSPENDED) == 0)
|
||||
{
|
||||
/* suspend pnode->src, and fire it after PNODE_COALESCE_TIME */
|
||||
pnode->flags |= PATH_SRC_SUSPENDED;
|
||||
dispatch_suspend(pnode->src);
|
||||
|
||||
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, PNODE_COALESCE_TIME);
|
||||
_path_node_retain(pnode);
|
||||
|
||||
dispatch_after(delay, get_notifyd_workloop(), ^{
|
||||
pnode->flags &= ~PATH_SRC_SUSPENDED;
|
||||
dispatch_resume(pnode->src);
|
||||
_path_node_release(pnode);
|
||||
});
|
||||
}
|
||||
|
||||
dispatch_source_merge_data(pnode->src, data);
|
||||
}
|
||||
}
|
||||
|
||||
buf = NULL;
|
||||
if (pnode->plen > MAXPATHLEN) buf = fixed;
|
||||
else buf = malloc(pnode->plen);
|
||||
assert(buf != NULL);
|
||||
|
||||
/* "autorelease" current sources (_vnode_sweep() will delete those with zero refcount) */
|
||||
_vnode_release_for_node(pnode);
|
||||
|
||||
/* create new sources (may re-use existing sources) */
|
||||
_vnode_create(NULL, 0, pnode);
|
||||
|
||||
memset(buf, 0, pnode->plen);
|
||||
for (i = 0; i < pnode->pname_count; i++)
|
||||
{
|
||||
assert((strlen(buf) + 1) <= pnode->plen);
|
||||
strlcat(buf, "/", pnode->plen);
|
||||
|
||||
assert(pnode->pname[i] != NULL);
|
||||
assert((strlen(buf) + strlen(pnode->pname[i])) <= pnode->plen);
|
||||
strlcat(buf, pnode->pname[i], pnode->plen);
|
||||
|
||||
memset(&sb, 0, sizeof(struct stat));
|
||||
if (lstat(buf, &sb) < 0)
|
||||
{
|
||||
/* the path stops existing here */
|
||||
break;
|
||||
}
|
||||
|
||||
if ((sb.st_mode & S_IFMT) == S_IFLNK)
|
||||
{
|
||||
/* open the symlink itself */
|
||||
_vnode_create(buf, VPATH_NODE_TYPE_LINK, pnode);
|
||||
|
||||
/* open the symlink target */
|
||||
_vnode_create_real_path(buf, 0, pnode);
|
||||
}
|
||||
else
|
||||
{
|
||||
_vnode_create(buf, 0, pnode);
|
||||
}
|
||||
}
|
||||
|
||||
/* sweep source list (deletes those with zero refcount) */
|
||||
_vnode_sweep();
|
||||
|
||||
if (buf != fixed) free(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a dispatch source that activates when a path changes.
|
||||
* Internally, creates a data structure (path_node_t) that represents the entire path.
|
||||
* Also creates dispatch sources (vnode_t) for each path component. These vnodes may
|
||||
* be shared with other path_node_t structures.
|
||||
*/
|
||||
path_node_t *
|
||||
path_node_create(const char *path, audit_token_t audit, bool is_notifyd, uint32_t mask)
|
||||
{
|
||||
path_node_t *pnode;
|
||||
dispatch_queue_t queue = get_notifyd_workloop();
|
||||
|
||||
dispatch_assert_queue(queue);
|
||||
|
||||
dispatch_once(&(_global.pathwatch_init), ^{ _pathwatch_init(); });
|
||||
|
||||
pnode = _path_node_init(path);
|
||||
if (pnode == NULL) return NULL;
|
||||
|
||||
pnode->refcount = 1;
|
||||
pnode->audit = audit;
|
||||
|
||||
_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)
|
||||
{
|
||||
pnode->flags |= PATH_NODE_CLIENT_NOTIFYD;
|
||||
}
|
||||
return pnode;
|
||||
}
|
84
notifyd/pathwatch.h
Normal file
84
notifyd/pathwatch.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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@
|
||||
*/
|
||||
|
||||
#ifndef _PATHWATCH_H_
|
||||
#define _PATHWATCH_H_
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
/*
|
||||
* types for virtual path nodes (path_node_t)
|
||||
*/
|
||||
#define PATH_NODE_TYPE_GHOST 0
|
||||
#define PATH_NODE_TYPE_FILE 1
|
||||
#define PATH_NODE_TYPE_LINK 2
|
||||
#define PATH_NODE_TYPE_DIR 3
|
||||
#define PATH_NODE_TYPE_OTHER 4
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PATH_NODE_DELETE = 0x0001, /* node or path deleted */
|
||||
PATH_NODE_WRITE = 0x0002, /* node written */
|
||||
PATH_NODE_EXTEND = 0x0004, /* node extended */
|
||||
PATH_NODE_ATTRIB = 0x0008, /* node attributes changed (mtime or ctime) */
|
||||
PATH_NODE_LINK = 0x0010, /* node link count changed */
|
||||
PATH_NODE_RENAME = 0x0020, /* node renamed, always accompanied by PATH_NODE_DELETE */
|
||||
PATH_NODE_REVOKE = 0x0040, /* access revoked, always accompanied by PATH_NODE_DELETE */
|
||||
PATH_NODE_CREATE = 0x0080, /* path created or access re-acquired */
|
||||
PATH_NODE_MTIME = 0x0100, /* path mtime changed, always accompanied by PATH_NODE_ATTRIB */
|
||||
PATH_NODE_CTIME = 0x0200 /* path ctime changed, always accompanied by PATH_NODE_ATTRIB */
|
||||
};
|
||||
|
||||
/* all bits mask */
|
||||
#define PATH_NODE_ALL 0x000003ff
|
||||
/* src is suspended */
|
||||
#define PATH_SRC_SUSPENDED 0x10000000
|
||||
/* the client is notifyd */
|
||||
#define PATH_NODE_CLIENT_NOTIFYD 0x20000000
|
||||
|
||||
/* Path changes coalesce for 100 milliseconds */
|
||||
#define PNODE_COALESCE_TIME 100000000
|
||||
|
||||
/*
|
||||
* path_node_t represents a virtual path
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *path;
|
||||
size_t plen;
|
||||
audit_token_t audit;
|
||||
uint32_t pname_count;
|
||||
char **pname;
|
||||
uint32_t type;
|
||||
uint32_t flags;
|
||||
dispatch_source_t src;
|
||||
void *contextp;
|
||||
uint32_t context32;
|
||||
uint64_t context64;
|
||||
uint32_t refcount;
|
||||
} path_node_t;
|
||||
|
||||
path_node_t *path_node_create(const char *path, audit_token_t audit, bool is_notifyd, uint32_t mask);
|
||||
void path_node_close(path_node_t *pnode);
|
||||
|
||||
#endif /* _PATHWATCH_H_ */
|
564
notifyd/service.c
Normal file
564
notifyd/service.c
Normal file
@ -0,0 +1,564 @@
|
||||
/*
|
||||
* Copyright (c) 2003-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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <asl.h>
|
||||
#include "notify.h"
|
||||
#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;
|
||||
|
||||
static uint32_t
|
||||
service_type(const char *name)
|
||||
{
|
||||
uint32_t len;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Request notifications for changes on a filesystem path.
|
||||
* This creates a new pathwatch node and sets it to post notifications for
|
||||
* the specified name.
|
||||
*
|
||||
* If the notify name already has a pathwatch node for this path, this routine
|
||||
* does nothing and allows the client to piggypack on the existing path watcher.
|
||||
*
|
||||
* Note that this routine is only called for path monitoring as directed by
|
||||
* a "monitor" command in /etc/notify.conf, so only an admin can set up a path
|
||||
* that gets public notifications. A client being serviced by the server-side
|
||||
* routines in notify_proc.c will only be able to register for a private
|
||||
* (per-client) notification for a path. This prevents a client from
|
||||
* piggybacking on another client's notifications, and thus prevents the client
|
||||
* from getting notifications for a path to which they don't have access.
|
||||
*/
|
||||
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++;
|
||||
|
||||
if (path == NULL) return NOTIFY_STATUS_INVALID_REQUEST;
|
||||
|
||||
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));
|
||||
node = path_node_create(path, audit, true, PATH_NODE_ALL);
|
||||
}
|
||||
|
||||
if (node == NULL) return NOTIFY_STATUS_PATH_NODE_CREATE_FAILED;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
dispatch_activate(node->src);
|
||||
|
||||
return NOTIFY_STATUS_OK;
|
||||
}
|
||||
|
||||
static uint16_t service_info_add(void *info)
|
||||
{
|
||||
assert(global.service_info_count != UINT16_MAX);
|
||||
|
||||
for(int i = 0; i < global.service_info_count; i++)
|
||||
{
|
||||
if(global.service_info_list[i] == NULL){
|
||||
global.service_info_list[i] = info;
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(global.service_info_count == 0){
|
||||
global.service_info_count = 1;
|
||||
global.service_info_list = malloc(sizeof(void *));
|
||||
} else {
|
||||
global.service_info_count++;
|
||||
global.service_info_list = realloc(global.service_info_list, global.service_info_count * sizeof(void *));
|
||||
}
|
||||
|
||||
global.service_info_list[global.service_info_count - 1] = info;
|
||||
|
||||
return global.service_info_count;
|
||||
}
|
||||
|
||||
void *service_info_get(uint16_t index)
|
||||
{
|
||||
if(index == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return global.service_info_list[index - 1];
|
||||
}
|
||||
|
||||
|
||||
static void *service_info_remove(uint16_t index)
|
||||
{
|
||||
if(index == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ret = global.service_info_list[index - 1];
|
||||
|
||||
global.service_info_list[index - 1] = NULL;
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The private (per-client) path watch service.
|
||||
* Must be callled on global.workloop if it is initialized
|
||||
*/
|
||||
int
|
||||
service_open_path_private(const char *name, client_t *c, const char *path, audit_token_t audit, uint32_t flags)
|
||||
{
|
||||
name_info_t *n;
|
||||
svc_info_t *info;
|
||||
path_node_t *node;
|
||||
|
||||
call_statistics.service_path++;
|
||||
|
||||
if (path == NULL) return NOTIFY_STATUS_INVALID_REQUEST;
|
||||
|
||||
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;
|
||||
|
||||
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_PATH_PRIVATE) 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 client is already getting notifications for this path */
|
||||
return NOTIFY_STATUS_OK;
|
||||
}
|
||||
|
||||
if (flags == 0) flags = PATH_NODE_ALL;
|
||||
|
||||
node = path_node_create(path, audit, false, flags);
|
||||
if (node == NULL) return NOTIFY_STATUS_PATH_NODE_CREATE_FAILED;
|
||||
|
||||
node->context64 = c->cid.hash_key;
|
||||
|
||||
info = (svc_info_t *)calloc(1, sizeof(svc_info_t));
|
||||
assert(info != NULL);
|
||||
|
||||
info->type = SERVICE_TYPE_PATH_PRIVATE;
|
||||
info->private = node;
|
||||
c->service_index = service_info_add(info);
|
||||
|
||||
dispatch_source_set_event_handler(node->src, ^{
|
||||
daemon_post_client(node->context64);
|
||||
});
|
||||
|
||||
dispatch_activate(node->src);
|
||||
|
||||
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)
|
||||
{
|
||||
uint32_t t, flags;
|
||||
char *p, *q;
|
||||
|
||||
t = service_type(name);
|
||||
|
||||
switch (t)
|
||||
{
|
||||
case SERVICE_TYPE_NONE:
|
||||
{
|
||||
return NOTIFY_STATUS_OK;
|
||||
}
|
||||
case SERVICE_TYPE_PATH_PRIVATE:
|
||||
{
|
||||
p = strchr(name, ':');
|
||||
if (p != NULL) p++;
|
||||
|
||||
flags = 0;
|
||||
|
||||
q = strchr(p, ':');
|
||||
if (q != NULL)
|
||||
{
|
||||
flags = (uint32_t)strtol(p, NULL, 0);
|
||||
p = q + 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return NOTIFY_STATUS_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
void
|
||||
service_close(uint16_t service_index)
|
||||
{
|
||||
if (service_index == 0) return;
|
||||
|
||||
svc_info_t *info = service_info_remove(service_index);
|
||||
|
||||
switch (info->type)
|
||||
{
|
||||
case SERVICE_TYPE_PATH_PUBLIC:
|
||||
case SERVICE_TYPE_PATH_PRIVATE:
|
||||
{
|
||||
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:
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
free(info);
|
||||
}
|
50
notifyd/service.h
Normal file
50
notifyd/service.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2003-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@
|
||||
*/
|
||||
|
||||
#ifndef _NOTIFY_SERVICE_H_
|
||||
#define _NOTIFY_SERVICE_H_
|
||||
|
||||
#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
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t type;
|
||||
void *private;
|
||||
} svc_info_t;
|
||||
|
||||
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);
|
||||
|
||||
#endif /* _NOTIFY_SERVICE_H_ */
|
409
notifyd/timer.c
Normal file
409
notifyd/timer.c
Normal file
@ -0,0 +1,409 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
56
notifyd/timer.h
Normal file
56
notifyd/timer.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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);
|
6
notifyd/xcodescripts/mk_notify_conf.sh
Normal file
6
notifyd/xcodescripts/mk_notify_conf.sh
Normal file
@ -0,0 +1,6 @@
|
||||
set -e -x
|
||||
ETCDIR="$DSTROOT"/private/etc
|
||||
install -d -o root -g wheel -m 0755 "$ETCDIR"
|
||||
install -c -o root -g wheel -m 0644 \
|
||||
"$SRCROOT"/notifyd/"$NOTIFY_CONFIG" \
|
||||
"$ETCDIR"/notify.conf
|
220
notifyutil/notifyutil.1
Normal file
220
notifyutil/notifyutil.1
Normal file
@ -0,0 +1,220 @@
|
||||
.\" Copyright (c) 2006-2011 Apple Inc. All rights reserved.
|
||||
.\"
|
||||
.\" @APPLE_LICENSE_HEADER_START@
|
||||
.\"
|
||||
.\" This file contains Original Code and/or Modifications of Original Code
|
||||
.\" as defined in and that are subject to the Apple Public Source License
|
||||
.\" Version 2.0 (the 'License'). You may not use this file except in
|
||||
.\" compliance with the License. Please obtain a copy of the License at
|
||||
.\" http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
.\" file.
|
||||
.\"
|
||||
.\" The Original Code and all software distributed under the License are
|
||||
.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
.\" Please see the License for the specific language governing rights and
|
||||
.\" limitations under the License.
|
||||
.\"
|
||||
.\" @APPLE_LICENSE_HEADER_END@
|
||||
.\"
|
||||
.\"
|
||||
.Dd November 4, 2011
|
||||
.Dt notifyutil 1
|
||||
.Os "Mac OS X"
|
||||
.Sh NAME
|
||||
.Nm notifyutil
|
||||
.Nd notification command line utility
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl q
|
||||
.Op Fl v
|
||||
.Op Fl z Ar msec
|
||||
.Op Fl M
|
||||
.Op Fl R
|
||||
.Op command Li ...
|
||||
.Pp
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is a command-line utility for interacting with the
|
||||
.Xr notify 3
|
||||
notification system and the
|
||||
.Xr notifyd 8
|
||||
server.
|
||||
It may be used to post notifications, detect and report notifications,
|
||||
and to examine and set the state values associated with notification keys.
|
||||
.Pp
|
||||
If
|
||||
.Nm
|
||||
is used to monitor one or more notification keys,
|
||||
it prints the notification key when the corresponding notification is received.
|
||||
The
|
||||
.Fl v
|
||||
(verbose)
|
||||
and
|
||||
.Fl q
|
||||
(quiet) flags, if specified, modify the output behavior.
|
||||
.Pp
|
||||
The
|
||||
.Fl v
|
||||
flag causes
|
||||
.Nm
|
||||
to print a time stamp, the notification key, the current state value for that key,
|
||||
and the type of the notification (port, file, etc).
|
||||
The
|
||||
.Fl q
|
||||
flag supresses any output except for state values fetched following a
|
||||
.Fl g
|
||||
command.
|
||||
.Pp
|
||||
Commands listed in the table below are processed in left to right order from the command line.
|
||||
.Pp
|
||||
.Bl -tag -width "-signal [#]" -compact -offset indent
|
||||
.It Fl p Ar key
|
||||
Post a notification for
|
||||
.Ar key .
|
||||
.It Fl w Ar key
|
||||
Register for
|
||||
.Ar key
|
||||
and wait forever for notifications.
|
||||
.It Fl Ar # Ar key
|
||||
Register for
|
||||
.Ar key
|
||||
and wait for
|
||||
.Ar #
|
||||
(an integer) notifications.
|
||||
.It ""
|
||||
.Li E.g.
|
||||
.Fl 1 Ar key
|
||||
waits for a single notification.
|
||||
.It Fl g Ar key
|
||||
Get state value for
|
||||
.Ar key .
|
||||
.It Fl s Ar key Ar val
|
||||
Set state value for
|
||||
.Ar key .
|
||||
.It Fl port
|
||||
Use mach port notifications for subsequent
|
||||
.Fl w
|
||||
or
|
||||
.Fl Ar #
|
||||
registrations.
|
||||
.It ""
|
||||
This is the default registration type.
|
||||
.It Fl file
|
||||
Use file descriptor notifications for subsequent registrations.
|
||||
.It Fl check
|
||||
Use shared memory notifications for subsequent registrations.
|
||||
.It Fl signal Op Ar #
|
||||
Use signal notifications for subsequent registrations.
|
||||
.It ""
|
||||
Signal 1 (HUP) is the default, but an alternate signal may be specified.
|
||||
.It Fl dispatch
|
||||
Use dispatch for subsequent registrations.
|
||||
.El
|
||||
.Pp
|
||||
When invoked with any combination of
|
||||
.Fl w
|
||||
and
|
||||
.Fl Ar #
|
||||
actions,
|
||||
.Nm
|
||||
registers for notification for the specified key(s).
|
||||
If any key is given with a
|
||||
.Fl w
|
||||
action,
|
||||
.Nm
|
||||
runs until interrupted with Control-C.
|
||||
If all registrations are invoked with
|
||||
.Fl Ar # ,
|
||||
the program continues to run until the corresponding number of notifications for each key have been received.
|
||||
.Pp
|
||||
By default,
|
||||
.Nm
|
||||
uses mach port registration (using
|
||||
.Fn notify_register_mach_port )
|
||||
for keys given with a
|
||||
.Fl w
|
||||
or
|
||||
.Fl Ar #
|
||||
flag.
|
||||
The
|
||||
.Fl file
|
||||
command causes
|
||||
.Nm
|
||||
to use
|
||||
.Fn notify_register_file_descriptor
|
||||
for any subsequent
|
||||
.Fl w
|
||||
or
|
||||
.Fl Ar #
|
||||
registrations.
|
||||
Similarly,
|
||||
.Fl check
|
||||
causes
|
||||
.Nm
|
||||
to use
|
||||
.Fn notify_register_check
|
||||
for subsequent registrations,
|
||||
.Fl signal
|
||||
switches to
|
||||
.Fn notify_register_signal ,
|
||||
and
|
||||
.Fl dispatch
|
||||
causes it to use
|
||||
.Fn notify_register_dispatch
|
||||
for subsequent registrations.
|
||||
.Pp
|
||||
If any registrations are made following the use of the
|
||||
.Fl check
|
||||
command,
|
||||
.Nm
|
||||
will start a timer and check for shared memory notifications every 100 milliseconds.
|
||||
An alternate timer value may be set following the
|
||||
.Fl z
|
||||
flag.
|
||||
.Pp
|
||||
The
|
||||
.Fl M
|
||||
flag causes
|
||||
.Nm
|
||||
to use multiplex all notifications over a single mach connection with
|
||||
.Nm notifyd .
|
||||
Notifications (except shared memory notifications)
|
||||
are received and redistributed by a dispatch handler.
|
||||
.Pp
|
||||
The
|
||||
.Fl R
|
||||
flag causes
|
||||
.Nm notifyutil
|
||||
to regenerate all its registrations in the unlikely event that
|
||||
.Nm notifyd
|
||||
restarts.
|
||||
.Pp
|
||||
Note that a notification key and its associated state variable only exist
|
||||
when there are one or more current registrations for that key.
|
||||
Setting the state for a key that has no registrations has no effect.
|
||||
Thus the command
|
||||
.Pp
|
||||
.Dl notifyutil -s foo.bar 123 -g foo.bar
|
||||
.Pp
|
||||
will print
|
||||
.Pp
|
||||
.Dl foo.bar 0
|
||||
.Pp
|
||||
unless foo.bar is registered by some other process.
|
||||
However, the command
|
||||
.Pp
|
||||
.Dl notifyutil -w foo.bar -s foo.bar 123 -g foo.bar
|
||||
.Pp
|
||||
prints
|
||||
.Pp
|
||||
.Dl foo.bar 123
|
||||
.Pp
|
||||
since the
|
||||
.Dq -w foo.bar
|
||||
registration ensures the key and its state variable exist before the value is set,
|
||||
and continue to exist when the value is fetched.
|
||||
.Sh SEE ALSO
|
||||
notify(3), notifyd(8)
|
755
notifyutil/notifyutil.c
Normal file
755
notifyutil/notifyutil.c
Normal file
@ -0,0 +1,755 @@
|
||||
/*
|
||||
* Copyright (c) 2006-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 <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <mach/mach.h>
|
||||
#include <notify.h>
|
||||
#include <notify_private.h>
|
||||
#include <signal.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <os/variant_private.h>
|
||||
|
||||
#define forever for(;;)
|
||||
#define IndexNull ((uint32_t)-1)
|
||||
|
||||
#define PRINT_QUIET 0x00000000
|
||||
#define PRINT_KEY 0x00000001
|
||||
#define PRINT_STATE 0x00000002
|
||||
#define PRINT_TIME 0x00000004
|
||||
#define PRINT_TYPE 0x00000008
|
||||
#define PRINT_TOKEN 0x00000010
|
||||
#define PRINT_VERBOSE 0xffffffff
|
||||
|
||||
#ifndef USEC_PER_SEC
|
||||
#define USEC_PER_SEC 1000000
|
||||
#endif
|
||||
|
||||
#define TYPE_NULL 0
|
||||
#define TYPE_PORT 1
|
||||
#define TYPE_FILE 2
|
||||
#define TYPE_DISPATCH 3
|
||||
#define TYPE_SIGNAL 4
|
||||
#define TYPE_CHECK 5
|
||||
#define TYPE_PLAIN 6
|
||||
|
||||
static const char *typename[] =
|
||||
{
|
||||
"unknown",
|
||||
"port",
|
||||
"file",
|
||||
"dispatch",
|
||||
"signal",
|
||||
"check",
|
||||
"plain"
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t token;
|
||||
uint32_t type;
|
||||
uint32_t signum;
|
||||
uint32_t count;
|
||||
char *name;
|
||||
} reg_entry_t;
|
||||
|
||||
static reg_entry_t *reg;
|
||||
static uint32_t reg_count = 0;
|
||||
|
||||
static int printopt;
|
||||
static int port_flag;
|
||||
static int file_flag;
|
||||
static int watch_file;
|
||||
static mach_port_t watch_port;
|
||||
static dispatch_source_t timer_src;
|
||||
static dispatch_source_t port_src;
|
||||
static dispatch_source_t file_src;
|
||||
static dispatch_source_t sig_src[__DARWIN_NSIG];
|
||||
static dispatch_queue_t watch_queue;
|
||||
|
||||
static void
|
||||
usage(const char *name)
|
||||
{
|
||||
fprintf(stderr, "usage: %s [-q] [-v] [-z msec] [-M] [-R] [command ...]\n", name);
|
||||
fprintf(stderr, " -q quiet mode\n");
|
||||
fprintf(stderr, " -v verbose - prints time, key, state value, and type\n");
|
||||
fprintf(stderr, " -z msec pause msec milliseconds after posting [default 100]\n");
|
||||
fprintf(stderr, " -M multiplex notifications from notifyd over a single mach port\n");
|
||||
fprintf(stderr, " -R regenerate registrations if notifyd restarts\n");
|
||||
fprintf(stderr, "commands:\n");
|
||||
fprintf(stderr, " -port switch to mach port for subsequent registrations [default]\n");
|
||||
fprintf(stderr, " -file switch to file descriptor for subsequent registrations\n");
|
||||
fprintf(stderr, " -check switch to shared memory for subsequent registrations\n");
|
||||
fprintf(stderr, " -signal [#] switch to signal [#] for subsequent registrations\n");
|
||||
fprintf(stderr, " initial default for signal is 1 (SIGHUP)\n");
|
||||
fprintf(stderr, " -dispatch switch to dispatch for subsequent registrations\n");
|
||||
fprintf(stderr, " -p key post a notification for key\n");
|
||||
fprintf(stderr, " -w key register for key and report notifications\n");
|
||||
fprintf(stderr, " -# key (# is an integer value, eg \"-1\") register for key and report # notifications\n");
|
||||
fprintf(stderr, " -g key get state value for key\n");
|
||||
fprintf(stderr, " -s key val set state value for key\n");
|
||||
|
||||
if(os_variant_has_internal_diagnostics(NULL))
|
||||
{
|
||||
fprintf(stderr, " --dump dumps metadata to a file in /var/run/\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Triggers a notifyd dump
|
||||
static void
|
||||
notifyutil_dump()
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = notify_dump_status("/var/run/notifyd.status");
|
||||
|
||||
if(ret == NOTIFY_STATUS_OK)
|
||||
{
|
||||
fprintf(stdout, "Notifyd dump success! New file created at /var/run/notifyd.status\n");
|
||||
} else {
|
||||
fprintf(stdout, "Notifyd dump failed with %x\n", ret);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
reg_add(uint32_t tid, uint32_t type, uint32_t signum, uint32_t count, const char *name)
|
||||
{
|
||||
if (name == NULL) return;
|
||||
|
||||
reg = (reg_entry_t *)reallocf(reg, (reg_count + 1) * sizeof(reg_entry_t));
|
||||
if (reg == NULL)
|
||||
{
|
||||
fprintf(stderr, "Can't allocate memory!\n");
|
||||
reg_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
reg[reg_count].token = tid;
|
||||
reg[reg_count].type = type;
|
||||
reg[reg_count].signum = signum;
|
||||
reg[reg_count].count = count;
|
||||
reg[reg_count].name = strdup(name);
|
||||
if (reg[reg_count].name == NULL)
|
||||
{
|
||||
fprintf(stderr, "Can't allocate memory!\n");
|
||||
reg = NULL;
|
||||
reg_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
reg_count++;
|
||||
}
|
||||
|
||||
static void
|
||||
reg_delete(uint32_t index)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (index == IndexNull) return;
|
||||
if (index >= reg_count) return;
|
||||
|
||||
free(reg[index].name);
|
||||
|
||||
for (i = index + 1; i < reg_count; i++) reg[i - 1] = reg[i];
|
||||
reg_count--;
|
||||
|
||||
if (reg_count == 0)
|
||||
{
|
||||
free(reg);
|
||||
reg = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
reg = (reg_entry_t *)reallocf(reg, reg_count * sizeof(reg_entry_t));
|
||||
if (reg == NULL)
|
||||
{
|
||||
fprintf(stderr, "Can't allocate memory!\n");
|
||||
reg_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
reg_find_token(uint32_t tid)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < reg_count; i++) if (tid == reg[i].token) return i;
|
||||
return IndexNull;
|
||||
}
|
||||
|
||||
static void
|
||||
process_event(int tid)
|
||||
{
|
||||
struct timeval now;
|
||||
char tstr[32];
|
||||
int status, index, needspace;
|
||||
uint64_t state;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
index = reg_find_token(tid);
|
||||
if (index == IndexNull) return;
|
||||
|
||||
needspace = 0;
|
||||
|
||||
if (printopt & PRINT_TOKEN)
|
||||
{
|
||||
printf("[%d]", tid);
|
||||
needspace = 1;
|
||||
}
|
||||
|
||||
if (printopt & PRINT_TIME)
|
||||
{
|
||||
if (needspace) printf(" ");
|
||||
snprintf(tstr, sizeof(tstr), "%llu", now.tv_usec + USEC_PER_SEC + 500);
|
||||
tstr[4] = '\0';
|
||||
printf("%d.%s", (int)now.tv_sec, tstr+1);
|
||||
needspace = 1;
|
||||
}
|
||||
|
||||
if (printopt & PRINT_KEY)
|
||||
{
|
||||
if (needspace) printf(" ");
|
||||
printf("%s", reg[index].name);
|
||||
needspace = 1;
|
||||
}
|
||||
|
||||
if (printopt & PRINT_STATE)
|
||||
{
|
||||
if (needspace) printf(" ");
|
||||
state = 0;
|
||||
status = notify_get_state(tid, &state);
|
||||
if (status == NOTIFY_STATUS_OK) printf("%llu",(unsigned long long)state);
|
||||
else printf(": Failed with code %d", status);
|
||||
needspace = 1;
|
||||
}
|
||||
|
||||
if (printopt & PRINT_TYPE)
|
||||
{
|
||||
if (needspace) printf(" ");
|
||||
printf("%s", typename[reg[index].type]);
|
||||
needspace = 1;
|
||||
}
|
||||
|
||||
if (printopt != PRINT_QUIET) printf("\n");
|
||||
|
||||
if ((reg[index].count != IndexNull) && (reg[index].count != 0)) reg[index].count--;
|
||||
if (reg[index].count == 0)
|
||||
{
|
||||
status = notify_cancel(tid);
|
||||
reg_delete(index);
|
||||
}
|
||||
fflush(stdout);
|
||||
|
||||
if (reg_count == 0) exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
file_handler(int fd)
|
||||
{
|
||||
ssize_t i;
|
||||
int tid;
|
||||
|
||||
if (fd < 0) return;
|
||||
|
||||
i = read(fd, &tid, sizeof(tid));
|
||||
if (i < 0) return;
|
||||
|
||||
tid = ntohl(tid);
|
||||
process_event(tid);
|
||||
|
||||
if (reg_count == 0) exit(0);
|
||||
}
|
||||
|
||||
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;
|
||||
process_event(tid);
|
||||
|
||||
if (reg_count == 0) exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
signal_handler(uint32_t sig)
|
||||
{
|
||||
uint32_t i, status;
|
||||
int check;
|
||||
|
||||
if (printopt != PRINT_QUIET) printf("SIGNAL %u\n", sig);
|
||||
for (i = 0; i < reg_count; i++)
|
||||
{
|
||||
if ((reg[i].type == TYPE_SIGNAL) && (reg[i].signum == sig))
|
||||
{
|
||||
check = 0;
|
||||
status = notify_check(reg[i].token, &check);
|
||||
if ((status == NOTIFY_STATUS_OK) && (check != 0)) process_event(reg[i].token);
|
||||
}
|
||||
}
|
||||
|
||||
if (reg_count == 0) exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch_handler(int x)
|
||||
{
|
||||
uint32_t index = reg_find_token(x);
|
||||
if (index == IndexNull) return;
|
||||
|
||||
process_event(reg[index].token);
|
||||
}
|
||||
|
||||
static void
|
||||
timer_handler(void)
|
||||
{
|
||||
uint32_t i, status;
|
||||
int check;
|
||||
|
||||
for (i = 0; i < reg_count; i++)
|
||||
{
|
||||
if ((reg[i].type == TYPE_CHECK) || (reg[i].type == TYPE_PLAIN))
|
||||
{
|
||||
check = 0;
|
||||
status = notify_check(reg[i].token, &check);
|
||||
if ((status == NOTIFY_STATUS_OK) && (check != 0)) process_event(reg[i].token);
|
||||
}
|
||||
}
|
||||
|
||||
if (reg_count == 0) exit(0);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
do_register(const char *name, uint32_t type, uint32_t signum, uint32_t count)
|
||||
{
|
||||
int tid, check;
|
||||
uint32_t status;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TYPE_PORT:
|
||||
{
|
||||
status = notify_register_mach_port(name, &watch_port, port_flag, &tid);
|
||||
if (status != NOTIFY_STATUS_OK) return status;
|
||||
|
||||
port_flag = NOTIFY_REUSE;
|
||||
if (port_src == NULL)
|
||||
{
|
||||
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_resume(port_src);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_FILE:
|
||||
{
|
||||
status = notify_register_file_descriptor(name, &watch_file, file_flag, &tid);
|
||||
if (status != NOTIFY_STATUS_OK) return status;
|
||||
|
||||
file_flag = NOTIFY_REUSE;
|
||||
if (file_src == NULL)
|
||||
{
|
||||
file_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)watch_file, 0, watch_queue);
|
||||
dispatch_source_set_event_handler(file_src, ^{
|
||||
file_handler(watch_file);
|
||||
});
|
||||
dispatch_resume(file_src);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_SIGNAL:
|
||||
{
|
||||
signal(signum, SIG_IGN);
|
||||
|
||||
status = notify_register_signal(name, signum, &tid);
|
||||
if (status != NOTIFY_STATUS_OK) return status;
|
||||
|
||||
status = notify_check(tid, &check);
|
||||
|
||||
if (sig_src[signum] == NULL)
|
||||
{
|
||||
sig_src[signum] = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)signum, 0, watch_queue);
|
||||
dispatch_source_set_event_handler(sig_src[signum], ^{
|
||||
signal_handler(signum);
|
||||
});
|
||||
dispatch_resume(sig_src[signum]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_DISPATCH:
|
||||
{
|
||||
status = notify_register_dispatch(name, &tid, watch_queue, ^(int x){ dispatch_handler(x); });
|
||||
if (status != NOTIFY_STATUS_OK) return status;
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_CHECK:
|
||||
{
|
||||
status = notify_register_check(name, &tid);
|
||||
if (status != NOTIFY_STATUS_OK) return status;
|
||||
|
||||
status = notify_check(tid, &check);
|
||||
|
||||
if (timer_src == NULL)
|
||||
{
|
||||
timer_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, watch_queue);
|
||||
dispatch_source_set_event_handler(timer_src, ^{
|
||||
timer_handler();
|
||||
});
|
||||
dispatch_source_set_timer(timer_src, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 10), NSEC_PER_SEC / 10, 0);
|
||||
dispatch_resume(timer_src);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_PLAIN:
|
||||
{
|
||||
status = notify_register_plain(name, &tid);
|
||||
if (status != NOTIFY_STATUS_OK) return status;
|
||||
|
||||
status = notify_check(tid, &check);
|
||||
|
||||
if (timer_src == NULL)
|
||||
{
|
||||
timer_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, watch_queue);
|
||||
dispatch_source_set_event_handler(timer_src, ^{
|
||||
timer_handler();
|
||||
});
|
||||
dispatch_source_set_timer(timer_src, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 10), NSEC_PER_SEC / 10, 0);
|
||||
dispatch_resume(timer_src);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: return NOTIFY_STATUS_FAILED;
|
||||
}
|
||||
|
||||
reg_add(tid, type, signum, count, name);
|
||||
return NOTIFY_STATUS_OK;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char *argv[])
|
||||
{
|
||||
const char *name;
|
||||
uint32_t i, n, signum, ntype, status, opts, nap;
|
||||
int tid;
|
||||
uint64_t state;
|
||||
|
||||
for (i = 0; i < __DARWIN_NSIG; i++) sig_src[i] = NULL;
|
||||
|
||||
ntype = TYPE_PORT;
|
||||
signum = 1;
|
||||
watch_file = -1;
|
||||
watch_port = MACH_PORT_NULL;
|
||||
printopt = PRINT_KEY;
|
||||
opts = 0;
|
||||
nap = 100000;
|
||||
|
||||
watch_queue = dispatch_queue_create("Watch Q", NULL);
|
||||
|
||||
name = strrchr(argv[0], '/');
|
||||
if (name == NULL) name = argv[0];
|
||||
else name++;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "-h")))
|
||||
{
|
||||
usage(name);
|
||||
exit(0);
|
||||
}
|
||||
else if (!strcmp(argv[i], "-q"))
|
||||
{
|
||||
printopt = PRINT_QUIET;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-v"))
|
||||
{
|
||||
printopt |= PRINT_VERBOSE;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-M"))
|
||||
{
|
||||
opts |= NOTIFY_OPT_DISPATCH;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-R"))
|
||||
{
|
||||
opts |= NOTIFY_OPT_REGEN;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-z"))
|
||||
{
|
||||
if ((i + 1) >= argc)
|
||||
{
|
||||
fprintf(stderr, "timer value must be supplied following -z\n");
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
if ((argv[i][0] < '0') || (argv[i][1] > '9'))
|
||||
{
|
||||
fprintf(stderr, "-z %s is invalid\n", argv[i]);
|
||||
fprintf(stderr, "timer value must be an integer\n");
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
nap = 1000 * atoi(argv[i]);
|
||||
}
|
||||
else if (!strcmp(argv[i], "-port"))
|
||||
{}
|
||||
else if (!strcmp(argv[i], "-file"))
|
||||
{}
|
||||
else if ((!strcmp(argv[i], "-sig")) || (!strcmp(argv[i], "-signal")))
|
||||
{
|
||||
if ((i + 1) >= argc) continue;
|
||||
if (argv[i + 1][0] == '-') continue;
|
||||
|
||||
i++;
|
||||
|
||||
if ((argv[i][0] < '0') || (argv[i][1] > '9'))
|
||||
{
|
||||
fprintf(stderr, "-signal %s is invalid\n", argv[i]);
|
||||
fprintf(stderr, "signals must be specified as integer values\n");
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
else if (!strcmp(argv[i], "-dispatch"))
|
||||
{}
|
||||
else if (!strcmp(argv[i], "-check"))
|
||||
{}
|
||||
else if (!strcmp(argv[i], "-plain"))
|
||||
{}
|
||||
else if (!strcmp(argv[i], "-p"))
|
||||
{
|
||||
if ((i + 1) >= argc)
|
||||
{
|
||||
fprintf(stderr, "name required following -p\n");
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
else if ((argv[i][0] == '-') && ((argv[i][1] == 'w') || ((argv[i][1] >= '0') && (argv[i][1] <= '9'))))
|
||||
{
|
||||
if ((i + 1) >= argc)
|
||||
{
|
||||
fprintf(stderr, "name required following %s\n", argv[i]);
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-g"))
|
||||
{
|
||||
if ((i + 1) >= argc)
|
||||
{
|
||||
fprintf(stderr, "name required following -g\n");
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-s"))
|
||||
{
|
||||
if ((i + 1) >= argc)
|
||||
{
|
||||
fprintf(stderr, "name required following -s\n");
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
if ((i + 1) >= argc)
|
||||
{
|
||||
fprintf(stderr, "value required following -s name\n");
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i++;
|
||||
state = atoll(argv[i]);
|
||||
if ((state == 0) && (strcmp(argv[i], "0")))
|
||||
{
|
||||
fprintf(stderr, "value following -s name must be a 64-bit integer\n");
|
||||
}
|
||||
}
|
||||
else if (!strcmp(argv[i], "--dump") && os_variant_has_internal_diagnostics(NULL))
|
||||
{
|
||||
notifyutil_dump();
|
||||
exit(0);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "unrecognized option: %s\n", argv[i]);
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts != 0) notify_set_options(opts);
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if (!strcmp(argv[i], "-port"))
|
||||
{
|
||||
ntype = TYPE_PORT;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-file"))
|
||||
{
|
||||
ntype = TYPE_FILE;
|
||||
}
|
||||
else if ((!strcmp(argv[i], "-sig")) || (!strcmp(argv[i], "-signal")))
|
||||
{
|
||||
|
||||
ntype = TYPE_SIGNAL;
|
||||
if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
|
||||
{
|
||||
i++;
|
||||
signum = atoi(argv[i]);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(argv[i], "-dispatch"))
|
||||
{
|
||||
ntype = TYPE_DISPATCH;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-check"))
|
||||
{
|
||||
ntype = TYPE_CHECK;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-plain"))
|
||||
{
|
||||
ntype = TYPE_PLAIN;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-p"))
|
||||
{
|
||||
if ((i + 1) >= argc)
|
||||
{
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
status = notify_post(argv[i]);
|
||||
if (status != NOTIFY_STATUS_OK) printf("%s: Failed with code %d\n", argv[i], status);
|
||||
else if (nap > 0) usleep(nap);
|
||||
}
|
||||
else if ((argv[i][0] == '-') && ((argv[i][1] == 'w') || ((argv[i][1] >= '0') && (argv[i][1] <= '9'))))
|
||||
{
|
||||
if ((i + 1) >= argc)
|
||||
{
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
n = IndexNull;
|
||||
if (argv[i][1] != 'w') n = atoi(argv[i] + 1);
|
||||
|
||||
i++;
|
||||
tid = IndexNull;
|
||||
|
||||
status = do_register(argv[i], ntype, signum, n);
|
||||
if (status != NOTIFY_STATUS_OK) printf("%s: Failed with code %d\n", argv[i], status);
|
||||
}
|
||||
else if (!strcmp(argv[i], "-g"))
|
||||
{
|
||||
if ((i + 1) >= argc)
|
||||
{
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i++;
|
||||
state = 0;
|
||||
tid = IndexNull;
|
||||
|
||||
status = notify_register_plain(argv[i], &tid);
|
||||
if (status == NOTIFY_STATUS_OK)
|
||||
{
|
||||
status = notify_get_state(tid, &state);
|
||||
notify_cancel(tid);
|
||||
}
|
||||
|
||||
if (status == NOTIFY_STATUS_OK) printf("%s %llu\n", argv[i], (unsigned long long)state);
|
||||
else printf("%s: Failed with code %d\n", argv[i], status);
|
||||
}
|
||||
else if (!strcmp(argv[i], "-s"))
|
||||
{
|
||||
if ((i + 2) >= argc)
|
||||
{
|
||||
usage(name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i++;
|
||||
tid = IndexNull;
|
||||
status = notify_register_plain(argv[i], &tid);
|
||||
if (status == NOTIFY_STATUS_OK)
|
||||
{
|
||||
state = atoll(argv[i + 1]);
|
||||
status = notify_set_state(tid, state);
|
||||
notify_cancel(tid);
|
||||
}
|
||||
|
||||
if (status != NOTIFY_STATUS_OK) printf("%s: Failed with code %d\n", argv[i], status);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (reg_count == 0) exit(0);
|
||||
|
||||
dispatch_main();
|
||||
}
|
8
notifyutil/notifyutil_entitlements.plist
Normal file
8
notifyutil/notifyutil_entitlements.plist
Normal file
@ -0,0 +1,8 @@
|
||||
<?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>com.apple.private.libnotify.statecapture</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
124
table.c
Normal file
124
table.c
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#include <os/base.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <os/assumes.h>
|
||||
|
||||
#include "table.h"
|
||||
#include "notify_internal.h"
|
||||
|
||||
#define TABLE_TOMBSTONE ((void *)~0)
|
||||
#define TABLE_MINSHIFT 5
|
||||
#define TABLE_MINSIZE (1 << TABLE_MINSHIFT)
|
||||
|
||||
OS_ALWAYS_INLINE
|
||||
static inline uint32_t
|
||||
table_next(uint32_t i, uint32_t size)
|
||||
{
|
||||
i++;
|
||||
return i >= size ? 0 : i;
|
||||
}
|
||||
|
||||
OS_ALWAYS_INLINE
|
||||
static inline uint32_t
|
||||
table_prev(uint32_t i, uint32_t size)
|
||||
{
|
||||
return (i ? i : size) - 1;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
string_equals(const char *a, const char *b)
|
||||
{
|
||||
return a == b || strcmp(a, b) == 0;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
string_hash(const char *key)
|
||||
{
|
||||
uint32_t hash = 0;
|
||||
|
||||
for (; *key; key++) {
|
||||
hash += (unsigned char)(*key);
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
uint32_equals(uint32_t a, uint32_t b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
uint32_hash(uint32_t x)
|
||||
{
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
uint64_equals(uint64_t a, uint64_t b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
uint64_hash(uint64_t key)
|
||||
{
|
||||
return uint32_hash((uint32_t)key ^ (uint32_t)(key >> 32));
|
||||
}
|
||||
|
||||
#define ns(n) _nc_table##n
|
||||
#define key_t char *
|
||||
#define ckey_t const char *
|
||||
#define key_hash string_hash
|
||||
#define key_equals string_equals
|
||||
#include "table.in.c"
|
||||
|
||||
#define ns(n) _nc_table##n##_n
|
||||
#define key_t uint32_t
|
||||
#define ckey_t uint32_t
|
||||
#define key_hash uint32_hash
|
||||
#define key_equals uint32_equals
|
||||
#include "table.in.c"
|
||||
|
||||
#define ns(n) _nc_table##n##_64
|
||||
#define key_t uint64_t
|
||||
#define ckey_t uint64_t
|
||||
#define key_hash uint64_hash
|
||||
#define key_equals uint64_equals
|
||||
#include "table.in.c"
|
68
table.h
Normal file
68
table.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#ifndef _NOTIFY_TABLE_H_
|
||||
#define _NOTIFY_TABLE_H_
|
||||
|
||||
#include <os/base.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define _nc_table(key_t, _ns) \
|
||||
struct _nc_table##_ns { \
|
||||
uint32_t count; \
|
||||
uint32_t tombstones; \
|
||||
uint32_t size; \
|
||||
uint16_t grow_shift; \
|
||||
uint16_t key_offset; \
|
||||
key_t **keys; \
|
||||
}
|
||||
|
||||
typedef _nc_table(char *, ) table_t;
|
||||
typedef _nc_table(uint32_t, _n) table_n_t;
|
||||
typedef _nc_table(uint64_t, _64) table_64_t;
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
extern void _nc_table_init(table_t *t, size_t key_offset);
|
||||
extern void _nc_table_init_n(table_n_t *t, size_t key_offset);
|
||||
extern void _nc_table_init_64(table_64_t *t, size_t key_offset);
|
||||
|
||||
extern void _nc_table_insert(table_t *t, char **key);
|
||||
extern void _nc_table_insert_n(table_n_t *t, uint32_t *key);
|
||||
extern void _nc_table_insert_64(table_64_t *t, uint64_t *key);
|
||||
|
||||
extern void *_nc_table_find(table_t *t, const char *key);
|
||||
extern void *_nc_table_find_n(table_n_t *t, uint32_t key);
|
||||
extern void *_nc_table_find_64(table_64_t *t, uint64_t key);
|
||||
|
||||
extern void _nc_table_delete(table_t *t, const char *key);
|
||||
extern void _nc_table_delete_n(table_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 *));
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* _NOTIFY_TABLE_H_ */
|
209
table.in.c
Normal file
209
table.in.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Apple Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
static inline void *
|
||||
ns(_value)(struct ns() *t, uint32_t i)
|
||||
{
|
||||
return (void *)((uintptr_t)t->keys[i] - t->key_offset);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ns(_clear)(struct ns() *t)
|
||||
{
|
||||
free(t->keys);
|
||||
ns(_init)(t, t->key_offset);
|
||||
}
|
||||
|
||||
void
|
||||
ns(_init)(struct ns() *t, size_t offset)
|
||||
{
|
||||
*t = (struct ns()){
|
||||
.grow_shift = TABLE_MINSHIFT,
|
||||
.key_offset = (uint16_t)offset,
|
||||
};
|
||||
}
|
||||
|
||||
OS_NOINLINE
|
||||
static void
|
||||
ns(_rehash)(struct ns() *t, int direction)
|
||||
{
|
||||
struct ns() old = *t;
|
||||
|
||||
if (direction > 0) {
|
||||
t->size += (1 << t->grow_shift);
|
||||
if (t->size == (8 << t->grow_shift)) {
|
||||
t->grow_shift++;
|
||||
}
|
||||
} else if (direction < 0) {
|
||||
if (t->grow_shift > TABLE_MINSHIFT) {
|
||||
t->grow_shift--;
|
||||
}
|
||||
t->size = roundup(t->size / 2, (1 << t->grow_shift));
|
||||
}
|
||||
|
||||
t->count = 0;
|
||||
t->tombstones = 0;
|
||||
t->keys = calloc(t->size, sizeof(key_t *));
|
||||
if (t->keys == NULL) {
|
||||
NOTIFY_INTERNAL_CRASH(0, "Unable to grow table: registration leak?");
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < old.size; i++) {
|
||||
if (old.keys[i] == NULL || old.keys[i] == TABLE_TOMBSTONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ns(_insert)(t, old.keys[i]);
|
||||
}
|
||||
free(old.keys);
|
||||
}
|
||||
|
||||
void *
|
||||
ns(_find)(struct ns() *t, ckey_t key)
|
||||
{
|
||||
if (t->count == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t size = t->size, loop_limit = t->size;
|
||||
uint32_t i = key_hash(key) % size;
|
||||
|
||||
for (;;) {
|
||||
if (os_unlikely(loop_limit-- == 0)) {
|
||||
NOTIFY_INTERNAL_CRASH(0, "Corrupt hash table");
|
||||
}
|
||||
if (t->keys[i] != TABLE_TOMBSTONE) {
|
||||
if (t->keys[i] == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (key_equals(key, *t->keys[i])) {
|
||||
return ns(_value)(t, i);
|
||||
}
|
||||
}
|
||||
i = table_next(i, size);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ns(_insert)(struct ns() *t, key_t *key)
|
||||
{
|
||||
/*
|
||||
* Our algorithm relies on having enough NULLS to end loops.
|
||||
* Make sure their density is never below 25%.
|
||||
*
|
||||
* When it drops too low, if the ratio of tombstones is low,
|
||||
* assume we're on a growth codepath.
|
||||
*
|
||||
* Else, we just rehash in place to prune tombstones.
|
||||
*/
|
||||
if (os_unlikely(t->count + t->tombstones >= 3 * t->size / 4)) {
|
||||
if (t->count >= 4 * t->tombstones) {
|
||||
ns(_rehash)(t, 1);
|
||||
} else {
|
||||
ns(_rehash)(t, 0);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t size = t->size, loop_limit = t->size;
|
||||
uint32_t i = key_hash(*key) % size;
|
||||
|
||||
for (;;) {
|
||||
if (os_unlikely(loop_limit-- == 0)) {
|
||||
NOTIFY_INTERNAL_CRASH(0, "Corrupt hash table");
|
||||
}
|
||||
if (t->keys[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
if (t->keys[i] == TABLE_TOMBSTONE) {
|
||||
t->tombstones--;
|
||||
break;
|
||||
}
|
||||
i = table_next(i, size);
|
||||
}
|
||||
|
||||
t->keys[i] = key;
|
||||
t->count++;
|
||||
}
|
||||
|
||||
void
|
||||
ns(_delete)(struct ns() *t, ckey_t key)
|
||||
{
|
||||
if (t->count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t size = t->size, loop_limit = t->size;
|
||||
uint32_t i = key_hash(key) % size;
|
||||
|
||||
for (;;) {
|
||||
if (os_unlikely(loop_limit-- == 0)) {
|
||||
NOTIFY_INTERNAL_CRASH(0, "Corrupt hash table");
|
||||
}
|
||||
if (t->keys[i] != TABLE_TOMBSTONE) {
|
||||
if (t->keys[i] == NULL) {
|
||||
return;
|
||||
}
|
||||
if (key_equals(key, *t->keys[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
i = table_next(i, size);
|
||||
}
|
||||
|
||||
t->keys[i] = TABLE_TOMBSTONE;
|
||||
t->tombstones++;
|
||||
t->count--;
|
||||
|
||||
if (t->keys[table_next(i, size)] == NULL) {
|
||||
do {
|
||||
t->tombstones--;
|
||||
t->keys[i] = NULL;
|
||||
i = table_prev(i, size);
|
||||
} while (t->keys[i] == TABLE_TOMBSTONE);
|
||||
}
|
||||
|
||||
if (t->count == 0) {
|
||||
/* if the table is empty, free all its resources */
|
||||
ns(_clear)(t);
|
||||
} else if (t->size >= TABLE_MINSIZE * 2 && t->count < t->size / 8) {
|
||||
/* if the table density drops below 12%, shrink it */
|
||||
ns(_rehash)(t, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ns(_foreach)(struct ns() *t, bool (^handler)(void *))
|
||||
{
|
||||
for (uint32_t i = 0; i < t->size; i++) {
|
||||
if (t->keys[i] != NULL && t->keys[i] != TABLE_TOMBSTONE) {
|
||||
if (!handler(ns(_value)(t, i))) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef ns
|
||||
#undef key_t
|
||||
#undef ckey_t
|
||||
#undef key_hash
|
||||
#undef key_equals
|
||||
#undef make_map
|
33
xcodeconfig/base.xcconfig
Normal file
33
xcodeconfig/base.xcconfig
Normal file
@ -0,0 +1,33 @@
|
||||
#include "<DEVELOPER_DIR>/Makefiles/CoreOS/Xcode/BSD.xcconfig"
|
||||
|
||||
// Architectures
|
||||
SDKROOT = macosx.internal
|
||||
|
||||
ALWAYS_SEARCH_USER_PATHS = YES
|
||||
HEADER_SEARCH_PATHS = $(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders $(PROJECT_DIR)
|
||||
ARCHS = $(ARCHS_STANDARD_32_64_BIT)
|
||||
CODE_SIGN_IDENTITY = -
|
||||
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
|
||||
GCC_ENABLE_CPP_EXCEPTIONS = NO
|
||||
GCC_ENABLE_CPP_RTTI = NO
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = NO
|
||||
GCC_PREPROCESSOR_DEFINITIONS = __DARWIN_NON_CANCELABLE=1
|
||||
GCC_C_LANGUAGE_STANDARD = 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
|
||||
OTHER_CFLAGS_debug = -O0
|
||||
CURRENT_PROJECT_VERSION =
|
||||
CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion)
|
||||
VERSION_INFO_PREFIX = __
|
||||
VERSIONING_SYSTEM = apple-generic
|
||||
PREBINDING = NO
|
||||
LLVM_LTO = YES
|
||||
|
||||
NOTIFY_CONFIG = notify.conf.MacOSX
|
||||
NOTIFY_CONFIG[sdk=iphoneos*] = notify.conf.iPhone
|
||||
NOTIFY_CONFIG[sdk=tvos*] = notify.conf.iPhone
|
||||
NOTIFY_CONFIG[sdk=*simulator*] = notify.conf.iOSSimulator
|
35
xcodeconfig/libnotify.xcconfig
Normal file
35
xcodeconfig/libnotify.xcconfig
Normal file
@ -0,0 +1,35 @@
|
||||
#include "base.xcconfig"
|
||||
|
||||
PRODUCT_NAME = libsystem_notify
|
||||
INSTALL_PATH = /usr/lib/system
|
||||
PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include
|
||||
PUBLIC_HEADERS_FOLDER_PATH = /usr/include
|
||||
DYLIB_CURRENT_VERSION = $(CURRENT_PROJECT_VERSION)
|
||||
EXECUTABLE_PREFIX =
|
||||
BUILD_VARIANTS = normal
|
||||
CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion)
|
||||
VERSION_INFO_PREFIX = __
|
||||
VERSIONING_SYSTEM = apple-generic
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = YES
|
||||
STRIP_STYLE = non-global
|
||||
SUPPORTS_TEXT_BASED_API = YES
|
||||
TAPI_VERIFY_MODE = Pedantic
|
||||
|
||||
LINK_WITH_STANDARD_LIBRARIES = NO
|
||||
OTHER_LDFLAGS = -umbrella System -L/usr/lib/system $(LDFLAGS_DYLD) $(LDFLAGS_COMPILER_RT) $(LDFLAGS_SYSCALL) $(LDFLAGS_PLATFORM) $(LDFLAGS_PTHREAD) $(LDFLAGS_MALLOC) $(LDFLAGS_C) $(LDFLAGS_BLOCKS) $(LDFLAGS_DISPATCH) $(LDFLAGS_XPC) $(LDFLAGS_DARWIN)
|
||||
LDFLAGS_DYLD = -ldyld
|
||||
LDFLAGS_COMPILER_RT = -lcompiler_rt
|
||||
LDFLAGS_SYSCALL = -lsystem_kernel
|
||||
LDFLAGS_SYSCALL[sdk=iphonesimulator*] = -lsystem_sim_kernel
|
||||
LDFLAGS_PLATFORM = -lsystem_platform
|
||||
LDFLAGS_PLATFORM[sdk=iphonesimulator*] = -lsystem_sim_platform
|
||||
LDFLAGS_PTHREAD = -lsystem_pthread
|
||||
LDFLAGS_PTHREAD[sdk=iphonesimulator*] = -lsystem_sim_pthread
|
||||
LDFLAGS_MALLOC = -lsystem_malloc
|
||||
LDFLAGS_C = -lsystem_c
|
||||
LDFLAGS_BLOCKS = -lsystem_blocks
|
||||
LDFLAGS_DISPATCH = -ldispatch
|
||||
LDFLAGS_XPC = -lxpc
|
||||
LDFLAGS_DARWIN = -lsystem_darwin
|
||||
|
||||
IS_ZIPPERED=YES
|
11
xcodeconfig/notifyd.xcconfig
Normal file
11
xcodeconfig/notifyd.xcconfig
Normal file
@ -0,0 +1,11 @@
|
||||
#include "base.xcconfig"
|
||||
|
||||
INSTALL_PATH = /usr/sbin
|
||||
|
||||
// Sandbox settings
|
||||
SANDBOX_PATH = /System/Library/Sandbox/Profiles
|
||||
SANDBOX_PATH[sdk=iphone*] =
|
||||
SANDBOX_NAME = com.apple.notifyd
|
||||
SANDBOX_PROFILE = com.apple.notifyd.sb
|
||||
OTHER_MIGFLAGS = -DMACH_NOTIFY_SEND_POSSIBLE_EXPECTED=1
|
||||
|
4
xcodeconfig/notifyutil.xcconfig
Normal file
4
xcodeconfig/notifyutil.xcconfig
Normal file
@ -0,0 +1,4 @@
|
||||
#include "base.xcconfig"
|
||||
|
||||
INSTALL_PATH = /usr/bin
|
||||
|
5
xcodescripts/no-sim-man.sh
Normal file
5
xcodescripts/no-sim-man.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
if [[ "${PLATFORM_NAME}" =~ "simulator" ]]; then
|
||||
rm -rf ${DSTROOT}/usr/share/man
|
||||
fi
|
5
xcodescripts/sim-compat-symlink.sh
Normal file
5
xcodescripts/sim-compat-symlink.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
if [[ "${PLATFORM_NAME}" =~ "simulator" ]]; then
|
||||
ln -s libsystem_notify.dylib ${DSTROOT}${INSTALL_PATH}/libnotify_sim.dylib
|
||||
fi
|
22
xctests/Info.plist
Normal file
22
xctests/Info.plist
Normal file
@ -0,0 +1,22 @@
|
||||
<?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>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
162
xctests/RegisterTests.m
Normal file
162
xctests/RegisterTests.m
Normal file
@ -0,0 +1,162 @@
|
||||
//
|
||||
// xctests.m
|
||||
// xctests
|
||||
//
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <notify.h>
|
||||
#include <stdatomic.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "notify_internal.h"
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface RegisterTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation RegisterTests
|
||||
|
||||
static dispatch_queue_t noteQueue;
|
||||
|
||||
+ (void)setUp
|
||||
{
|
||||
noteQueue = dispatch_queue_create("noteQ", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
|
||||
}
|
||||
|
||||
+ (void)tearDown
|
||||
{
|
||||
noteQueue = nil;
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)test00RegisterSimple
|
||||
{
|
||||
static int token;
|
||||
XCTAssert(notify_register_dispatch("com.example.test.simple", &token, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(token) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
}
|
||||
|
||||
- (void)test01RegisterNested
|
||||
{
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
static int token1, token2;
|
||||
XCTAssert(notify_register_dispatch("com.example.test.multiple", &token1, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_register_dispatch("com.example.test.multiple", &token2, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(token1) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
XCTAssert(notify_cancel(token2) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)test02RegisterInterleaved
|
||||
{
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
static int token1, token2;
|
||||
XCTAssert(notify_register_dispatch("com.example.test.interleaved", &token1, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(token1) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
XCTAssert(notify_register_dispatch("com.example.test.interleaved", &token2, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(token2) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)test03RegisterRaceWithDealloc
|
||||
{
|
||||
static int tokens[1000000];
|
||||
dispatch_apply(1000000, DISPATCH_APPLY_AUTO, ^(size_t i) {
|
||||
XCTAssert(notify_register_check("com.example.test.race", &tokens[i]) == NOTIFY_STATUS_OK, @"notify_register_check failed");
|
||||
XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
XCTAssert(notify_register_dispatch("com.example.test.race", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
XCTAssert(notify_post("com.example.test.race") == NOTIFY_STATUS_OK, @"notify_post failed");
|
||||
});
|
||||
}
|
||||
|
||||
- (void)test04RegisterManyTokens
|
||||
{
|
||||
static int tokens[100000];
|
||||
dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) {
|
||||
XCTAssert(notify_register_dispatch("com.example.test.many", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
});
|
||||
}
|
||||
|
||||
- (void)test05RegisterBulkCancel
|
||||
{
|
||||
static int tokens[100000];
|
||||
dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) {
|
||||
XCTAssert(notify_register_dispatch("com.example.test.bulk", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
});
|
||||
|
||||
dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) {
|
||||
XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
});
|
||||
}
|
||||
|
||||
- (void)test06RegisterSimpleSelf
|
||||
{
|
||||
static int token;
|
||||
XCTAssert(notify_register_dispatch("self.example.test.simple", &token, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(token) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
}
|
||||
|
||||
- (void)test07RegisterNestedSelf
|
||||
{
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
static int token1, token2;
|
||||
XCTAssert(notify_register_dispatch("self.example.test.multiple", &token1, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_register_dispatch("self.example.test.multiple", &token2, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(token1) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
XCTAssert(notify_cancel(token2) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)test08RegisterInterleavedSelf
|
||||
{
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
static int token1, token2;
|
||||
XCTAssert(notify_register_dispatch("self.example.test.interleaved", &token1, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(token1) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
XCTAssert(notify_register_dispatch("self.example.test.interleaved", &token2, noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(token2) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)test09RegisterRaceWithDeallocSelf
|
||||
{
|
||||
static int tokens[1000000];
|
||||
dispatch_apply(1000000, DISPATCH_APPLY_AUTO, ^(size_t i) {
|
||||
XCTAssert(notify_register_check("self.example.test.race", &tokens[i]) == NOTIFY_STATUS_OK, @"notify_register_check failed");
|
||||
XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
XCTAssert(notify_register_dispatch("self.example.test.race", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
XCTAssert(notify_post("com.example.test.race") == NOTIFY_STATUS_OK, @"notify_post failed");
|
||||
});
|
||||
}
|
||||
|
||||
- (void)test10RegisterManyTokensSelf
|
||||
{
|
||||
static int tokens[100000];
|
||||
dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) {
|
||||
XCTAssert(notify_register_dispatch("self.example.test.many", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
});
|
||||
}
|
||||
|
||||
- (void)test11RegisterBulkCancelSelf
|
||||
{
|
||||
static int tokens[100000];
|
||||
dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) {
|
||||
XCTAssert(notify_register_dispatch("self.example.test.bulk", &tokens[i], noteQueue, ^(int i){}) == NOTIFY_STATUS_OK, @"notify_register_dispatch failed");
|
||||
});
|
||||
|
||||
dispatch_apply(100000, DISPATCH_APPLY_AUTO, ^(size_t i) {
|
||||
XCTAssert(notify_cancel(tokens[i]) == NOTIFY_STATUS_OK, @"notify_cancel failed");
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue
Block a user