Transfer Libnotify code from main repo

Based on `Libnotify-241`
This commit is contained in:
Thomas A 2022-04-23 14:09:31 -07:00
commit 2526674fb5
60 changed files with 15936 additions and 0 deletions

372
APPLE_LICENSE Normal file
View 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
View 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)

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

226
libnotify.h Normal file
View 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
View 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
View 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
View File

@ -0,0 +1 @@
.so man3/notify.3

1
notify_check.3 Normal file
View File

@ -0,0 +1 @@
.so man3/notify.3

4148
notify_client.c Normal file

File diff suppressed because it is too large Load Diff

1
notify_get_state.3 Normal file
View File

@ -0,0 +1 @@
.so man3/notify.3

153
notify_internal.h Normal file
View 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
View 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
View File

@ -0,0 +1 @@
.so man3/notify.3

83
notify_keys.h Normal file
View 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
View File

@ -0,0 +1 @@
.so man3/notify.3

70
notify_private.h Normal file
View 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
View 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
View 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
View File

@ -0,0 +1 @@
.so man3/notify.3

View File

@ -0,0 +1 @@
.so man3/notify.3

View File

@ -0,0 +1 @@
.so man3/notify.3

View File

@ -0,0 +1 @@
.so man3/notify.3

1
notify_register_signal.3 Normal file
View File

@ -0,0 +1 @@
.so man3/notify.3

1
notify_set_state.3 Normal file
View File

@ -0,0 +1 @@
.so man3/notify.3

451
notifybench/notify_bench.c Normal file
View 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
View 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)

View 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>

View 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)

View 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

View File

@ -0,0 +1,5 @@
#
# Notification Center configuration file
#
monitor com.apple.system.timezone /etc/localtime

View 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
View File

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

1
notifyd/notify_ipc.defs Symbolic link
View File

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

1350
notifyd/notify_proc.c Normal file

File diff suppressed because it is too large Load Diff

68
notifyd/notifyd.8 Normal file
View 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

File diff suppressed because it is too large Load Diff

119
notifyd/notifyd.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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);

View 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
View 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
View 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();
}

View 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
View 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
View 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
View 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
View 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

View 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

View 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

View File

@ -0,0 +1,4 @@
#include "base.xcconfig"
INSTALL_PATH = /usr/bin

View File

@ -0,0 +1,5 @@
#!/bin/bash -ex
if [[ "${PLATFORM_NAME}" =~ "simulator" ]]; then
rm -rf ${DSTROOT}/usr/share/man
fi

View 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
View 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
View 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