···11+APPLE PUBLIC SOURCE LICENSE
22+Version 1.1 - April 19,1999
33+44+Please read this License carefully before downloading this software.
55+By downloading and using this software, you are agreeing to be bound
66+by the terms of this License. If you do not or cannot agree to the
77+terms of this License, please do not download or use the software.
88+99+1. General; Definitions. This License applies to any program or other
1010+work which Apple Computer, Inc. ("Apple") publicly announces as
1111+subject to this Apple Public Source License and which contains a
1212+notice placed by Apple identifying such program or work as "Original
1313+Code" and stating that it is subject to the terms of this Apple Public
1414+Source License version 1.1 (or subsequent version thereof), as it may
1515+be revised from time to time by Apple ("License"). As used in this
1616+License:
1717+1818+1.1 "Affected Original Code" means only those specific portions of
1919+Original Code that allegedly infringe upon any party's intellectual
2020+property rights or are otherwise the subject of a claim of
2121+infringement.
2222+2323+1.2 "Applicable Patent Rights" mean: (a) in the case where Apple is
2424+the grantor of rights, (i) claims of patents that are now or hereafter
2525+acquired, owned by or assigned to Apple and (ii) that cover subject
2626+matter contained in the Original Code, but only to the extent
2727+necessary to use, reproduce and/or distribute the Original Code
2828+without infringement; and (b) in the case where You are the grantor of
2929+rights, (i) claims of patents that are now or hereafter acquired,
3030+owned by or assigned to You and (ii) that cover subject matter in Your
3131+Modifications, taken alone or in combination with Original Code.
3232+3333+1.3 "Covered Code" means the Original Code, Modifications, the
3434+combination of Original Code and any Modifications, and/or any
3535+respective portions thereof.
3636+3737+1.4 "Deploy" means to use, sublicense or distribute Covered Code other
3838+than for Your internal research and development (R&D), and includes
3939+without limitation, any and all internal use or distribution of
4040+Covered Code within Your business or organization except for R&D use,
4141+as well as direct or indirect sublicensing or distribution of Covered
4242+Code by You to any third party in any form or manner.
4343+4444+1.5 "Larger Work" means a work which combines Covered Code or portions
4545+thereof with code not governed by the terms of this License.
4646+4747+1.6 "Modifications" mean any addition to, deletion from, and/or change
4848+to, the substance and/or structure of Covered Code. When code is
4949+released as a series of files, a Modification is: (a) any addition to
5050+or deletion from the contents of a file containing Covered Code;
5151+and/or (b) any new file or other representation of computer program
5252+statements that contains any part of Covered Code.
5353+5454+1.7 "Original Code" means (a) the Source Code of a program or other
5555+work as originally made available by Apple under this License,
5656+including the Source Code of any updates or upgrades to such programs
5757+or works made available by Apple under this License, and that has been
5858+expressly identified by Apple as such in the header file(s) of such
5959+work; and (b) the object code compiled from such Source Code and
6060+originally made available by Apple under this License.
6161+6262+1.8 "Source Code" means the human readable form of a program or other
6363+work that is suitable for making modifications to it, including all
6464+modules it contains, plus any associated interface definition files,
6565+scripts used to control compilation and installation of an executable
6666+(object code).
6767+6868+1.9 "You" or "Your" means an individual or a legal entity exercising
6969+rights under this License. For legal entities, "You" or "Your"
7070+includes any entity which controls, is controlled by, or is under
7171+common control with, You, where "control" means (a) the power, direct
7272+or indirect, to cause the direction or management of such entity,
7373+whether by contract or otherwise, or (b) ownership of fifty percent
7474+(50%) or more of the outstanding shares or beneficial ownership of
7575+such entity.
7676+7777+2. Permitted Uses; Conditions & Restrictions. Subject to the terms
7878+and conditions of this License, Apple hereby grants You, effective on
7979+the date You accept this License and download the Original Code, a
8080+world-wide, royalty-free, non- exclusive license, to the extent of
8181+Apple's Applicable Patent Rights and copyrights covering the Original
8282+Code, to do the following:
8383+8484+2.1 You may use, copy, modify and distribute Original Code, with or
8585+without Modifications, solely for Your internal research and
8686+development, provided that You must in each instance:
8787+8888+(a) retain and reproduce in all copies of Original Code the copyright
8989+and other proprietary notices and disclaimers of Apple as they appear
9090+in the Original Code, and keep intact all notices in the Original Code
9191+that refer to this License;
9292+9393+(b) include a copy of this License with every copy of Source Code of
9494+Covered Code and documentation You distribute, and You may not offer
9595+or impose any terms on such Source Code that alter or restrict this
9696+License or the recipients' rights hereunder, except as permitted under
9797+Section 6; and
9898+9999+(c) completely and accurately document all Modifications that you have
100100+made and the date of each such Modification, designate the version of
101101+the Original Code you used, prominently include a file carrying such
102102+information with the Modifications, and duplicate the notice in
103103+Exhibit A in each file of the Source Code of all such Modifications.
104104+105105+2.2 You may Deploy Covered Code, provided that You must in each
106106+ instance:
107107+108108+(a) satisfy all the conditions of Section 2.1 with respect to the
109109+Source Code of the Covered Code;
110110+111111+(b) make all Your Deployed Modifications publicly available in Source
112112+Code form via electronic distribution (e.g. download from a web site)
113113+under the terms of this License and subject to the license grants set
114114+forth in Section 3 below, and any additional terms You may choose to
115115+offer under Section 6. You must continue to make the Source Code of
116116+Your Deployed Modifications available for as long as you Deploy the
117117+Covered Code or twelve (12) months from the date of initial
118118+Deployment, whichever is longer;
119119+120120+(c) if You Deploy Covered Code containing Modifications made by You,
121121+inform others of how to obtain those Modifications by filling out and
122122+submitting the information found at
123123+http://www.apple.com/publicsource/modifications.html, if available;
124124+and
125125+126126+(d) if You Deploy Covered Code in object code, executable form only,
127127+include a prominent notice, in the code itself as well as in related
128128+documentation, stating that Source Code of the Covered Code is
129129+available under the terms of this License with information on how and
130130+where to obtain such Source Code.
131131+132132+3. Your Grants. In consideration of, and as a condition to, the
133133+licenses granted to You under this License:
134134+135135+(a) You hereby grant to Apple and all third parties a non-exclusive,
136136+royalty-free license, under Your Applicable Patent Rights and other
137137+intellectual property rights owned or controlled by You, to use,
138138+reproduce, modify, distribute and Deploy Your Modifications of the
139139+same scope and extent as Apple's licenses under Sections 2.1 and 2.2;
140140+and
141141+142142+(b) You hereby grant to Apple and its subsidiaries a non-exclusive,
143143+worldwide, royalty-free, perpetual and irrevocable license, under Your
144144+Applicable Patent Rights and other intellectual property rights owned
145145+or controlled by You, to use, reproduce, execute, compile, display,
146146+perform, modify or have modified (for Apple and/or its subsidiaries),
147147+sublicense and distribute Your Modifications, in any form, through
148148+multiple tiers of distribution.
149149+150150+4. Larger Works. You may create a Larger Work by combining Covered
151151+Code with other code not governed by the terms of this License and
152152+distribute the Larger Work as a single product. In each such
153153+instance, You must make sure the requirements of this License are
154154+fulfilled for the Covered Code or any portion thereof.
155155+156156+5. Limitations on Patent License. Except as expressly stated in
157157+Section 2, no other patent rights, express or implied, are granted by
158158+Apple herein. Modifications and/or Larger Works may require
159159+additional patent licenses from Apple which Apple may grant in its
160160+sole discretion.
161161+162162+6. Additional Terms. You may choose to offer, and to charge a fee
163163+for, warranty, support, indemnity or liability obligations and/or
164164+other rights consistent with the scope of the license granted herein
165165+("Additional Terms") to one or more recipients of Covered
166166+Code. However, You may do so only on Your own behalf and as Your sole
167167+responsibility, and not on behalf of Apple. You must obtain the
168168+recipient's agreement that any such Additional Terms are offered by
169169+You alone, and You hereby agree to indemnify, defend and hold Apple
170170+harmless for any liability incurred by or claims asserted against
171171+Apple by reason of any such Additional Terms.
172172+173173+7. Versions of the License. Apple may publish revised and/or new
174174+versions of this License from time to time. Each version will be
175175+given a distinguishing version number. Once Original Code has been
176176+published under a particular version of this License, You may continue
177177+to use it under the terms of that version. You may also choose to use
178178+such Original Code under the terms of any subsequent version of this
179179+License published by Apple. No one other than Apple has the right to
180180+modify the terms applicable to Covered Code created under this
181181+License.
182182+183183+8. NO WARRANTY OR SUPPORT. The Original Code may contain in whole or
184184+in part pre-release, untested, or not fully tested works. The
185185+Original Code may contain errors that could cause failures or loss of
186186+data, and may be incomplete or contain inaccuracies. You expressly
187187+acknowledge and agree that use of the Original Code, or any portion
188188+thereof, is at Your sole and entire risk. THE ORIGINAL CODE IS
189189+PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND
190190+AND APPLE AND APPLE'S LICENSOR(S) (FOR THE PURPOSES OF SECTIONS 8 AND
191191+9, APPLE AND APPLE'S LICENSOR(S) ARE COLLECTIVELY REFERRED TO AS
192192+"APPLE") EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS
193193+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
194194+AND/OR CONDITIONS OF MERCHANTABILITY OR SATISFACTORY QUALITY AND
195195+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
196196+RIGHTS. APPLE DOES NOT WARRANT THAT THE FUNCTIONS CONTAINED IN THE
197197+ORIGINAL CODE WILL MEET YOUR REQUIREMENTS, OR THAT THE OPERATION OF
198198+THE ORIGINAL CODE WILL BE UNINTERRUPTED OR ERROR- FREE, OR THAT
199199+DEFECTS IN THE ORIGINAL CODE WILL BE CORRECTED. NO ORAL OR WRITTEN
200200+INFORMATION OR ADVICE GIVEN BY APPLE OR AN APPLE AUTHORIZED
201201+REPRESENTATIVE SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE
202202+SCOPE OF THIS WARRANTY. You acknowledge that the Original Code is not
203203+intended for use in the operation of nuclear facilities, aircraft
204204+navigation, communication systems, or air traffic control machines in
205205+which case the failure of the Original Code could lead to death,
206206+personal injury, or severe physical or environmental damage.
207207+208208+9. Liability.
209209+210210+9.1 Infringement. If any portion of, or functionality implemented by,
211211+the Original Code becomes the subject of a claim of infringement,
212212+Apple may, at its option: (a) attempt to procure the rights necessary
213213+for Apple and You to continue using the Affected Original Code; (b)
214214+modify the Affected Original Code so that it is no longer infringing;
215215+or (c) suspend Your rights to use, reproduce, modify, sublicense and
216216+distribute the Affected Original Code until a final determination of
217217+the claim is made by a court or governmental administrative agency of
218218+competent jurisdiction and Apple lifts the suspension as set forth
219219+below. Such suspension of rights will be effective immediately upon
220220+Apple's posting of a notice to such effect on the Apple web site that
221221+is used for implementation of this License. Upon such final
222222+determination being made, if Apple is legally able, without the
223223+payment of a fee or royalty, to resume use, reproduction,
224224+modification, sublicensing and distribution of the Affected Original
225225+Code, Apple will lift the suspension of rights to the Affected
226226+Original Code by posting a notice to such effect on the Apple web site
227227+that is used for implementation of this License. If Apple suspends
228228+Your rights to Affected Original Code, nothing in this License shall
229229+be construed to restrict You, at Your option and subject to applicable
230230+law, from replacing the Affected Original Code with non-infringing
231231+code or independently negotiating for necessary rights from such third
232232+party.
233233+234234+9.2 LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES SHALL APPLE BE
235235+LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES
236236+ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO
237237+USE THE ORIGINAL CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY
238238+OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY
239239+OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
240240+SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF
241241+ANY REMEDY. In no event shall Apple's total liability to You for all
242242+damages under this License exceed the amount of fifty dollars
243243+($50.00).
244244+245245+10. Trademarks. This License does not grant any rights to use the
246246+trademarks or trade names "Apple", "Apple Computer", "Mac OS X", "Mac
247247+OS X Server" or any other trademarks or trade names belonging to Apple
248248+(collectively "Apple Marks") and no Apple Marks may be used to endorse
249249+or promote products derived from the Original Code other than as
250250+permitted by and in strict compliance at all times with Apple's third
251251+party trademark usage guidelines which are posted at
252252+http://www.apple.com/legal/guidelinesfor3rdparties.html.
253253+254254+11. Ownership. Apple retains all rights, title and interest in and to
255255+the Original Code and any Modifications made by or on behalf of Apple
256256+("Apple Modifications"), and such Apple Modifications will not be
257257+automatically subject to this License. Apple may, at its sole
258258+discretion, choose to license such Apple Modifications under this
259259+License, or on different terms from those contained in this License or
260260+may choose not to license them at all. Apple's development, use,
261261+reproduction, modification, sublicensing and distribution of Covered
262262+Code will not be subject to this License.
263263+264264+12. Termination.
265265+266266+12.1 Termination. This License and the rights granted hereunder will
267267+ terminate:
268268+269269+(a) automatically without notice from Apple if You fail to comply with
270270+any term(s) of this License and fail to cure such breach within 30
271271+days of becoming aware of such breach; (b) immediately in the event of
272272+the circumstances described in Section 13.5(b); or (c) automatically
273273+without notice from Apple if You, at any time during the term of this
274274+License, commence an action for patent infringement against Apple.
275275+276276+12.2 Effect of Termination. Upon termination, You agree to
277277+immediately stop any further use, reproduction, modification,
278278+sublicensing and distribution of the Covered Code and to destroy all
279279+copies of the Covered Code that are in your possession or control.
280280+All sublicenses to the Covered Code which have been properly granted
281281+prior to termination shall survive any termination of this License.
282282+Provisions which, by their nature, should remain in effect beyond the
283283+termination of this License shall survive, including but not limited
284284+to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. Neither party will be
285285+liable to the other for compensation, indemnity or damages of any sort
286286+solely as a result of terminating this License in accordance with its
287287+terms, and termination of this License will be without prejudice to
288288+any other right or remedy of either party.
289289+290290+13. Miscellaneous.
291291+292292+13.1 Government End Users. The Covered Code is a "commercial item" as
293293+defined in FAR 2.101. Government software and technical data rights
294294+in the Covered Code include only those rights customarily provided to
295295+the public as defined in this License. This customary commercial
296296+license in technical data and software is provided in accordance with
297297+FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for
298298+Department of Defense purchases, DFAR 252.227-7015 (Technical Data --
299299+Commercial Items) and 227.7202-3 (Rights in Commercial Computer
300300+Software or Computer Software Documentation). Accordingly, all U.S.
301301+Government End Users acquire Covered Code with only those rights set
302302+forth herein.
303303+304304+13.2 Relationship of Parties. This License will not be construed as
305305+creating an agency, partnership, joint venture or any other form of
306306+legal association between You and Apple, and You will not represent to
307307+the contrary, whether expressly, by implication, appearance or
308308+otherwise.
309309+310310+13.3 Independent Development. Nothing in this License will impair
311311+Apple's right to acquire, license, develop, have others develop for
312312+it, market and/or distribute technology or products that perform the
313313+same or similar functions as, or otherwise compete with,
314314+Modifications, Larger Works, technology or products that You may
315315+develop, produce, market or distribute.
316316+317317+13.4 Waiver; Construction. Failure by Apple to enforce any provision
318318+of this License will not be deemed a waiver of future enforcement of
319319+that or any other provision. Any law or regulation which provides
320320+that the language of a contract shall be construed against the drafter
321321+will not apply to this License.
322322+323323+13.5 Severability. (a) If for any reason a court of competent
324324+jurisdiction finds any provision of this License, or portion thereof,
325325+to be unenforceable, that provision of the License will be enforced to
326326+the maximum extent permissible so as to effect the economic benefits
327327+and intent of the parties, and the remainder of this License will
328328+continue in full force and effect. (b) Notwithstanding the foregoing,
329329+if applicable law prohibits or restricts You from fully and/or
330330+specifically complying with Sections 2 and/or 3 or prevents the
331331+enforceability of either of those Sections, this License will
332332+immediately terminate and You must immediately discontinue any use of
333333+the Covered Code and destroy all copies of it that are in your
334334+possession or control.
335335+336336+13.6 Dispute Resolution. Any litigation or other dispute resolution
337337+between You and Apple relating to this License shall take place in the
338338+Northern District of California, and You and Apple hereby consent to
339339+the personal jurisdiction of, and venue in, the state and federal
340340+courts within that District with respect to this License. The
341341+application of the United Nations Convention on Contracts for the
342342+International Sale of Goods is expressly excluded.
343343+344344+13.7 Entire Agreement; Governing Law. This License constitutes the
345345+entire agreement between the parties with respect to the subject
346346+matter hereof. This License shall be governed by the laws of the
347347+United States and the State of California, except that body of
348348+California law concerning conflicts of law.
349349+350350+Where You are located in the province of Quebec, Canada, the following
351351+clause applies: The parties hereby confirm that they have requested
352352+that this License and all related documents be drafted in English. Les
353353+parties ont exige que le present contrat et tous les documents
354354+connexes soient rediges en anglais.
355355+356356+EXHIBIT A.
357357+358358+"Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
359359+Reserved. This file contains Original Code and/or Modifications of
360360+Original Code as defined in and that are subject to the Apple Public
361361+Source License Version 1.1 (the "License"). You may not use this file
362362+except in compliance with the License. Please obtain a copy of the
363363+License at http://www.apple.com/publicsource and read it before using
364364+this file.
365365+366366+The Original Code and all software distributed under the License are
367367+distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
368368+EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
369369+INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
370370+FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
371371+License for the specific language governing rights and limitations
372372+under the License."
···11+/*
22+ * Copyright (c) 2003-2012 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#include <sys/types.h>
2525+#include <stdio.h>
2626+#include <fcntl.h>
2727+#include <stdlib.h>
2828+#include <unistd.h>
2929+#include <sys/socket.h>
3030+#include <netinet/in.h>
3131+#include <sys/un.h>
3232+#include <sys/ipc.h>
3333+#include <signal.h>
3434+#include <mach/mach.h>
3535+#include <mach/mach_time.h>
3636+#include <errno.h>
3737+#include <pthread.h>
3838+3939+#include "libnotify.h"
4040+#include "notify.h"
4141+#include "notify_internal.h"
4242+4343+#define USER_PROTECTED_UID_PREFIX "user.uid."
4444+#define USER_PROTECTED_UID_PREFIX_LEN 9
4545+4646+uint64_t
4747+make_client_id(pid_t pid, int token)
4848+{
4949+ uint64_t cid;
5050+5151+ cid = pid;
5252+ cid <<= 32;
5353+ cid |= token;
5454+5555+ return cid;
5656+}
5757+5858+notify_state_t *
5959+_notify_lib_notify_state_new(uint32_t flags, uint32_t table_size)
6060+{
6161+ notify_state_t *ns;
6262+6363+ ns = (notify_state_t *)calloc(1, sizeof(notify_state_t));
6464+ if (ns == NULL) return NULL;
6565+6666+ ns->flags = flags;
6767+ ns->sock = -1;
6868+6969+ if (ns->flags & NOTIFY_STATE_USE_LOCKS)
7070+ {
7171+ ns->lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
7272+ if (ns->lock == NULL)
7373+ {
7474+ free(ns);
7575+ return NULL;
7676+ }
7777+7878+ pthread_mutex_init(ns->lock, NULL);
7979+ }
8080+8181+ ns->name_table = _nc_table_new(table_size);
8282+ ns->name_id_table = _nc_table_new(table_size);
8383+ ns->client_table = _nc_table_new(table_size);
8484+ ns->port_table = _nc_table_new(table_size);
8585+ ns->proc_table = _nc_table_new(table_size);
8686+8787+ if ((ns->name_table == NULL) || (ns->name_id_table == NULL) || (ns->client_table == NULL) || (ns->port_table == NULL) || (ns->proc_table == NULL))
8888+ {
8989+ free(ns->lock);
9090+ _nc_table_free(ns->name_table);
9191+ _nc_table_free(ns->name_id_table);
9292+ _nc_table_free(ns->client_table);
9393+ _nc_table_free(ns->port_table);
9494+ _nc_table_free(ns->proc_table);
9595+ free(ns);
9696+ return NULL;
9797+ }
9898+9999+ return ns;
100100+}
101101+102102+void
103103+_notify_lib_notify_state_free(notify_state_t *ns)
104104+{
105105+ if (ns == NULL) return;
106106+107107+ _nc_table_free(ns->name_table);
108108+ _nc_table_free(ns->name_id_table);
109109+ _nc_table_free(ns->client_table);
110110+ _nc_table_free(ns->port_table);
111111+ _nc_table_free(ns->proc_table);
112112+113113+ if (ns->lock != NULL)
114114+ {
115115+ pthread_mutex_destroy(ns->lock);
116116+ free(ns->lock);
117117+ }
118118+119119+ if (ns->sock != -1)
120120+ {
121121+ shutdown(ns->sock, 2);
122122+ close(ns->sock);
123123+ }
124124+125125+ if (ns->controlled_name != NULL) free(ns->controlled_name);
126126+}
127127+128128+static client_t *
129129+_internal_client_new(notify_state_t *ns, pid_t pid, int token)
130130+{
131131+ client_t *c;
132132+ uint64_t cid = make_client_id(pid, token);
133133+134134+ if (ns == NULL) return NULL;
135135+136136+ /* detect duplicates - should never happen, but it would be bad */
137137+ c = _nc_table_find_64(ns->client_table, cid);
138138+ if (c != NULL) return NULL;
139139+140140+ c = calloc(1, sizeof(client_t));
141141+ if (c == NULL) return NULL;
142142+143143+ ns->stat_client_alloc++;
144144+145145+ c->client_id = cid;
146146+ c->pid = pid;
147147+ c->send_val = token;
148148+149149+ _nc_table_insert_64(ns->client_table, cid, c);
150150+151151+ return c;
152152+}
153153+154154+static void
155155+_internal_client_release(notify_state_t *ns, client_t *c)
156156+{
157157+ uint64_t cid;
158158+159159+ if (ns == NULL) return;
160160+ if (c == NULL) return;
161161+162162+ cid = c->client_id;
163163+ _nc_table_delete_64(ns->client_table, cid);
164164+165165+ switch (c->notify_type)
166166+ {
167167+ case NOTIFY_TYPE_SIGNAL:
168168+ {
169169+ break;
170170+ }
171171+ case NOTIFY_TYPE_FILE:
172172+ {
173173+ if (c->fd > 0) close(c->fd);
174174+ c->fd = -1;
175175+ break;
176176+ }
177177+ case NOTIFY_TYPE_PORT:
178178+ {
179179+ if (c->port != MACH_PORT_NULL)
180180+ {
181181+ /* release my send right to the port */
182182+ mach_port_deallocate(mach_task_self(), c->port);
183183+ }
184184+ break;
185185+ }
186186+ default:
187187+ {
188188+ break;
189189+ }
190190+ }
191191+192192+ free(c);
193193+ ns->stat_client_free++;
194194+}
195195+196196+static name_info_t *
197197+_internal_new_name(notify_state_t *ns, const char *name)
198198+{
199199+ name_info_t *n;
200200+ size_t namelen;
201201+202202+ if (ns == NULL) return NULL;
203203+ if (name == NULL) return NULL;
204204+205205+ namelen = strlen(name) + 1;
206206+207207+ n = (name_info_t *)calloc(1, sizeof(name_info_t) + namelen);
208208+ if (n == NULL) return NULL;
209209+210210+ ns->stat_name_alloc++;
211211+212212+ n->name = (char *)n + sizeof(name_info_t);
213213+ memcpy(n->name, name, namelen);
214214+215215+ notify_globals_t globals = _notify_globals();
216216+ n->name_id = globals->name_id++;
217217+218218+ n->access = NOTIFY_ACCESS_DEFAULT;
219219+ n->slot = (uint32_t)-1;
220220+ n->val = 1;
221221+222222+ _nc_table_insert_no_copy(ns->name_table, n->name, n);
223223+ _nc_table_insert_64(ns->name_id_table, n->name_id, n);
224224+225225+ return n;
226226+}
227227+228228+static void
229229+_internal_insert_controlled_name(notify_state_t *ns, name_info_t *n)
230230+{
231231+ int i, j;
232232+233233+ if (ns == NULL) return;
234234+ if (n == NULL) return;
235235+236236+ if (ns->controlled_name == NULL) ns->controlled_name_count = 0;
237237+238238+ for (i = 0; i < ns->controlled_name_count; i++)
239239+ {
240240+ if (ns->controlled_name[i] == n) return;
241241+ }
242242+243243+ ns->controlled_name = (name_info_t **)reallocf(ns->controlled_name, (ns->controlled_name_count + 1) * sizeof(name_info_t *));
244244+245245+ /*
246246+ * Insert name in reverse sorted order (longer names preceed shorter names).
247247+ * this means that in _internal_check_access, we check subspaces from the bottom up
248248+ * i.e. we check access for the "deepest" controlled subspace.
249249+ */
250250+251251+ for (i = 0; i < ns->controlled_name_count; i++)
252252+ {
253253+ if (strcmp(n->name, ns->controlled_name[i]->name) > 0) break;
254254+ }
255255+256256+ for (j = ns->controlled_name_count; j > i; j--)
257257+ {
258258+ ns->controlled_name[j] = ns->controlled_name[j-1];
259259+ }
260260+261261+ ns->controlled_name[i] = n;
262262+ ns->controlled_name_count++;
263263+}
264264+265265+static void
266266+_internal_remove_controlled_name(notify_state_t *ns, name_info_t *n)
267267+{
268268+ uint32_t i, j;
269269+270270+ for (i = 0; i < ns->controlled_name_count; i++)
271271+ {
272272+ if (ns->controlled_name[i] == n)
273273+ {
274274+ for (j = i + 1; j < ns->controlled_name_count; j++)
275275+ {
276276+ ns->controlled_name[j-1] = ns->controlled_name[j];
277277+ }
278278+279279+ ns->controlled_name_count--;
280280+ if (ns->controlled_name_count == 0)
281281+ {
282282+ free(ns->controlled_name);
283283+ ns->controlled_name = NULL;
284284+ }
285285+ else
286286+ {
287287+ ns->controlled_name = (name_info_t **)reallocf(ns->controlled_name, ns->controlled_name_count * sizeof(name_info_t *));
288288+ }
289289+290290+ return;
291291+ }
292292+ }
293293+}
294294+295295+static uint32_t
296296+_internal_check_access(notify_state_t *ns, const char *name, uid_t uid, gid_t gid, int req)
297297+{
298298+ uint32_t i, len, plen;
299299+ name_info_t *p;
300300+ char str[64];
301301+302302+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
303303+ if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
304304+305305+ /* root may do anything */
306306+ if (uid == 0) return NOTIFY_STATUS_OK;
307307+308308+ /* if name has "user.uid." as a prefix, it is a user-protected namespace */
309309+ if (!strncmp(name, USER_PROTECTED_UID_PREFIX, USER_PROTECTED_UID_PREFIX_LEN))
310310+ {
311311+ snprintf(str, sizeof(str) - 1, "%s%d", USER_PROTECTED_UID_PREFIX, uid);
312312+ len = strlen(str);
313313+314314+ /* user <uid> may access user.uid.<uid> or a subtree name */
315315+ if ((!strncmp(name, str, len)) && ((name[len] == '\0') || (name[len] == '.'))) return NOTIFY_STATUS_OK;
316316+ return NOTIFY_STATUS_NOT_AUTHORIZED;
317317+ }
318318+319319+ len = strlen(name);
320320+321321+ if (ns->controlled_name == NULL) ns->controlled_name_count = 0;
322322+ for (i = 0; i < ns->controlled_name_count; i++)
323323+ {
324324+ p = ns->controlled_name[i];
325325+ if (p == NULL) break;
326326+ if (p->name == NULL) continue;
327327+328328+ plen = strlen(p->name);
329329+ if (plen > len) continue;
330330+ if (strncmp(p->name, name, plen)) continue;
331331+332332+ /* Found a match or a prefix, check if restrictions apply to this uid/gid */
333333+ if ((p->uid == uid) && (p->access & (req << NOTIFY_ACCESS_USER_SHIFT))) break;
334334+ if ((p->gid == gid) && (p->access & (req << NOTIFY_ACCESS_GROUP_SHIFT))) break;
335335+ if (p->access & (req << NOTIFY_ACCESS_OTHER_SHIFT)) break;
336336+337337+ return NOTIFY_STATUS_NOT_AUTHORIZED;
338338+ }
339339+340340+ return NOTIFY_STATUS_OK;
341341+}
342342+343343+uint32_t
344344+_notify_lib_check_controlled_access(notify_state_t *ns, char *name, uid_t uid, gid_t gid, int req)
345345+{
346346+ uint32_t status;
347347+348348+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
349349+350350+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
351351+ status = _internal_check_access(ns, name, uid, gid, req);
352352+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
353353+354354+ return status;
355355+}
356356+357357+uint32_t
358358+_notify_lib_port_proc_new(notify_state_t *ns, mach_port_t port, pid_t proc, uint32_t state, dispatch_source_t src)
359359+{
360360+ portproc_data_t *pdata;
361361+362362+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
363363+ if ((proc == 0) && (port == MACH_PORT_NULL)) return NOTIFY_STATUS_FAILED;
364364+365365+ pdata = (portproc_data_t *)calloc(1, sizeof(portproc_data_t));
366366+ if (pdata == NULL) return NOTIFY_STATUS_FAILED;
367367+368368+ ns->stat_portproc_alloc++;
369369+370370+ pdata->refcount = 1;
371371+ pdata->flags = state;
372372+ pdata->src = src;
373373+374374+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
375375+ if (proc == 0) _nc_table_insert_n(ns->port_table, port, pdata);
376376+ else _nc_table_insert_n(ns->proc_table, proc, pdata);
377377+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
378378+379379+ return NOTIFY_STATUS_OK;
380380+}
381381+382382+portproc_data_t *
383383+_notify_lib_port_proc_find(notify_state_t *ns, mach_port_t port, pid_t proc)
384384+{
385385+ portproc_data_t *pdata = NULL;
386386+387387+ if (ns == NULL) return NULL;
388388+ if ((proc == 0) && (port == MACH_PORT_NULL)) return NULL;
389389+390390+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
391391+392392+ if (proc == 0) pdata = _nc_table_find_n(ns->port_table, port);
393393+ else pdata = _nc_table_find_n(ns->proc_table, proc);
394394+395395+ if (pdata != NULL) pdata->refcount++;
396396+397397+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
398398+399399+ return pdata;
400400+}
401401+402402+void
403403+_notify_lib_port_proc_release(notify_state_t *ns, mach_port_t port, pid_t proc)
404404+{
405405+ portproc_data_t *pdata = NULL;
406406+407407+ if (ns == NULL) return;
408408+ if ((proc == 0) && (port == MACH_PORT_NULL)) return;
409409+410410+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
411411+412412+ if (proc == 0) pdata = _nc_table_find_n(ns->port_table, port);
413413+ else pdata = _nc_table_find_n(ns->proc_table, proc);
414414+415415+ if (pdata != NULL)
416416+ {
417417+ if (pdata->refcount > 0) pdata->refcount--;
418418+ if (pdata->refcount == 0)
419419+ {
420420+ if (proc == 0) _nc_table_delete_n(ns->port_table, port);
421421+ else _nc_table_delete_n(ns->proc_table, proc);
422422+423423+ dispatch_source_cancel(pdata->src);
424424+ dispatch_release(pdata->src);
425425+426426+ free(pdata);
427427+ ns->stat_portproc_free++;
428428+ }
429429+ }
430430+431431+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
432432+}
433433+434434+/*
435435+ * Send notification to a subscriber
436436+ */
437437+static uint32_t
438438+_internal_send(notify_state_t *ns, client_t *c)
439439+{
440440+ uint32_t send;
441441+ portproc_data_t *pdata;
442442+443443+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
444444+ if (c == NULL) return NOTIFY_STATUS_FAILED;
445445+446446+ if (c->state & NOTIFY_CLIENT_STATE_SUSPENDED)
447447+ {
448448+ c->state |= NOTIFY_CLIENT_STATE_PENDING;
449449+ return NOTIFY_STATUS_OK;
450450+ }
451451+452452+ pdata = _nc_table_find_n(ns->proc_table, c->pid);
453453+ if ((pdata != NULL) && (pdata->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED))
454454+ {
455455+ c->suspend_count++;
456456+ c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
457457+ c->state |= NOTIFY_CLIENT_STATE_PENDING;
458458+ return NOTIFY_STATUS_OK;
459459+ }
460460+461461+ send = c->send_val;
462462+463463+ switch (c->notify_type)
464464+ {
465465+ case NOTIFY_TYPE_SIGNAL:
466466+ {
467467+ int rc = 0;
468468+469469+ if (c->pid == NOTIFY_CLIENT_SELF) rc = kill(getpid(), c->sig);
470470+ else rc = kill(c->pid, c->sig);
471471+472472+ if (rc != 0) return NOTIFY_STATUS_FAILED;
473473+474474+ c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
475475+ c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
476476+477477+ return NOTIFY_STATUS_OK;
478478+ }
479479+480480+ case NOTIFY_TYPE_FILE:
481481+ {
482482+ ssize_t len;
483483+484484+ if (c->fd >= 0)
485485+ {
486486+ send = htonl(send);
487487+ len = write(c->fd, &send, sizeof(uint32_t));
488488+ if (len != sizeof(uint32_t))
489489+ {
490490+ close(c->fd);
491491+ c->fd = -1;
492492+ return NOTIFY_STATUS_FAILED;
493493+ }
494494+ }
495495+496496+ c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
497497+ c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
498498+499499+ return NOTIFY_STATUS_OK;
500500+ }
501501+502502+ case NOTIFY_TYPE_PORT:
503503+ {
504504+ kern_return_t kstatus;
505505+ mach_msg_empty_send_t msg;
506506+ mach_msg_option_t opts = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
507507+508508+ pdata = _nc_table_find_n(ns->port_table, c->port);
509509+ if ((pdata != NULL) && (pdata->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED))
510510+ {
511511+ c->suspend_count++;
512512+ c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
513513+ c->state |= NOTIFY_CLIENT_STATE_PENDING;
514514+ return NOTIFY_STATUS_OK;
515515+ }
516516+517517+ if (ns->flags & NOTIFY_STATE_ENABLE_RESEND) opts |= MACH_SEND_NOTIFY;
518518+519519+ memset(&msg, 0, sizeof(mach_msg_empty_send_t));
520520+ msg.header.msgh_size = sizeof(mach_msg_empty_send_t);
521521+ msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO);
522522+ msg.header.msgh_local_port = MACH_PORT_NULL;
523523+ msg.header.msgh_remote_port = c->port;
524524+ msg.header.msgh_id = (mach_msg_id_t)send;
525525+526526+ kstatus = mach_msg(&msg.header, opts, msg.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
527527+528528+ if (kstatus == MACH_SEND_TIMED_OUT)
529529+ {
530530+ /* deallocate port rights obtained via pseudo-receive after failed mach_msg() send */
531531+ mach_msg_destroy(&msg.header);
532532+ if (ns->flags & NOTIFY_STATE_ENABLE_RESEND)
533533+ {
534534+ /*
535535+ * Suspend on timeout.
536536+ * notifyd will get a MACH_NOTIFY_SEND_POSSIBLE and trigger a retry.
537537+ * c->suspend_count must be zero, or we would not be trying to send.
538538+ */
539539+ c->suspend_count++;
540540+ c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
541541+ c->state |= NOTIFY_CLIENT_STATE_PENDING;
542542+ c->state |= NOTIFY_CLIENT_STATE_TIMEOUT;
543543+544544+ return NOTIFY_STATUS_OK;
545545+ }
546546+547547+ return NOTIFY_STATUS_FAILED;
548548+ }
549549+ else if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
550550+551551+ c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
552552+ c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
553553+554554+ return NOTIFY_STATUS_OK;
555555+ }
556556+557557+ default:
558558+ {
559559+ break;
560560+ }
561561+ }
562562+563563+ c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
564564+ c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
565565+566566+ return NOTIFY_STATUS_OK;
567567+}
568568+569569+uint32_t
570570+_notify_lib_post_client(notify_state_t *ns, client_t *c)
571571+{
572572+ uint32_t status;
573573+574574+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
575575+ if (c == NULL) return NOTIFY_STATUS_FAILED;
576576+577577+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
578578+ status = _internal_send(ns, c);
579579+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
580580+581581+ return status;
582582+}
583583+584584+static uint32_t
585585+_internal_post_name(notify_state_t *ns, name_info_t *n, uid_t uid, gid_t gid)
586586+{
587587+ int auth;
588588+ list_t *l;
589589+ client_t *c;
590590+591591+ if (n == NULL) return NOTIFY_STATUS_INVALID_NAME;
592592+593593+ auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_WRITE);
594594+ if (auth != 0) return NOTIFY_STATUS_NOT_AUTHORIZED;
595595+596596+ n->val++;
597597+598598+ for (l = n->subscriptions; l != NULL; l = _nc_list_next(l))
599599+ {
600600+ c = _nc_list_data(l);
601601+ if (c != NULL) _internal_send(ns, c);
602602+ }
603603+604604+ return NOTIFY_STATUS_OK;
605605+}
606606+607607+/*
608608+ * Notify subscribers of this name.
609609+ */
610610+uint32_t
611611+_notify_lib_post(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
612612+{
613613+ name_info_t *n;
614614+ uint32_t status;
615615+616616+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
617617+618618+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
619619+620620+ n = (name_info_t *)_nc_table_find(ns->name_table, name);
621621+ if (n == NULL)
622622+ {
623623+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
624624+ return NOTIFY_STATUS_INVALID_NAME;
625625+ }
626626+627627+ status = _internal_post_name(ns, n, uid, gid);
628628+629629+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
630630+ return status;
631631+}
632632+633633+uint32_t
634634+_notify_lib_post_nid(notify_state_t *ns, uint64_t nid, uid_t uid, gid_t gid)
635635+{
636636+ name_info_t *n;
637637+ uint32_t status;
638638+639639+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
640640+641641+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
642642+643643+ n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid);
644644+ if (n == NULL)
645645+ {
646646+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
647647+ return NOTIFY_STATUS_INVALID_NAME;
648648+ }
649649+650650+ status = _internal_post_name(ns, n, uid, gid);
651651+652652+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
653653+ return status;
654654+}
655655+656656+static void
657657+_internal_release_name_info(notify_state_t *ns, name_info_t *n)
658658+{
659659+ if (ns == NULL) return;
660660+ if (n == NULL) return;
661661+662662+ if (n->refcount > 0) n->refcount--;
663663+ if (n->refcount == 0)
664664+ {
665665+ _internal_remove_controlled_name(ns, n);
666666+ _nc_table_delete(ns->name_table, n->name);
667667+ _nc_table_delete_64(ns->name_id_table, n->name_id);
668668+ _nc_list_release_list(n->subscriptions);
669669+ free(n);
670670+ ns->stat_name_free++;
671671+ }
672672+}
673673+674674+/*
675675+ * Cancel (delete) a client
676676+ */
677677+static void
678678+_internal_cancel(notify_state_t *ns, uint64_t cid)
679679+{
680680+ client_t *c;
681681+ name_info_t *n;
682682+683683+ if (ns == NULL) return;
684684+685685+ c = NULL;
686686+ n = NULL;
687687+688688+ c = _nc_table_find_64(ns->client_table, cid);
689689+ if (c == NULL) return;
690690+691691+ n = c->name_info;
692692+ if (n == NULL) return;
693693+694694+ n->subscriptions =_nc_list_find_release(n->subscriptions, c);
695695+ _internal_client_release(ns, c);
696696+ _internal_release_name_info(ns, n);
697697+}
698698+699699+void
700700+_notify_lib_cancel(notify_state_t *ns, pid_t pid, int token)
701701+{
702702+ uint64_t cid;
703703+704704+ if (ns == NULL) return;
705705+706706+ cid = make_client_id(pid, token);
707707+708708+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
709709+ _internal_cancel(ns, cid);
710710+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
711711+}
712712+713713+void
714714+_notify_lib_suspend(notify_state_t *ns, pid_t pid, int token)
715715+{
716716+ client_t *c;
717717+ uint64_t cid;
718718+719719+ if (ns == NULL) return;
720720+721721+ cid = make_client_id(pid, token);
722722+723723+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
724724+725725+ c = _nc_table_find_64(ns->client_table, cid);
726726+ if (c != NULL)
727727+ {
728728+ c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
729729+ if (c->suspend_count < UINT32_MAX) c->suspend_count++;
730730+ }
731731+732732+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
733733+}
734734+735735+uint32_t
736736+_notify_lib_resume(notify_state_t *ns, pid_t pid, int token)
737737+{
738738+ client_t *c;
739739+ uint64_t cid;
740740+ uint32_t status = NOTIFY_STATUS_OK;
741741+742742+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
743743+744744+ cid = make_client_id(pid, token);
745745+746746+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
747747+748748+ c = _nc_table_find_64(ns->client_table, cid);
749749+ if (c != NULL)
750750+ {
751751+ if (c->suspend_count > 0) c->suspend_count--;
752752+ if (c->suspend_count == 0)
753753+ {
754754+ c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED;
755755+ c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
756756+757757+ if (c->state & NOTIFY_CLIENT_STATE_PENDING)
758758+ {
759759+ status = _internal_send(ns, c);
760760+ }
761761+ }
762762+ }
763763+764764+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
765765+766766+ return status;
767767+}
768768+769769+void
770770+_notify_lib_suspend_proc(notify_state_t *ns, pid_t pid)
771771+{
772772+ portproc_data_t *pdata;
773773+774774+ if (ns == NULL) return;
775775+776776+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
777777+778778+ pdata = _nc_table_find_n(ns->proc_table, pid);
779779+ if (pdata != NULL) pdata->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED;
780780+781781+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
782782+}
783783+784784+void
785785+_notify_lib_resume_proc(notify_state_t *ns, pid_t pid)
786786+{
787787+ client_t *c;
788788+ void *tt;
789789+ portproc_data_t *pdata;
790790+791791+ if (ns == NULL) return;
792792+793793+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
794794+795795+ /* Resume all subscriptions for this process */
796796+ pdata = _nc_table_find_n(ns->proc_table, pid);
797797+ if (pdata != NULL) pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED;
798798+799799+ tt = _nc_table_traverse_start(ns->client_table);
800800+ while (tt != NULL)
801801+ {
802802+ c = _nc_table_traverse(ns->client_table, tt);
803803+ if (c == NULL) break;
804804+805805+ if (c->pid == pid)
806806+ {
807807+ if (c->suspend_count > 0) c->suspend_count--;
808808+ if (c->suspend_count == 0)
809809+ {
810810+ c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED;
811811+ c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
812812+813813+ if (c->state & NOTIFY_CLIENT_STATE_PENDING)
814814+ {
815815+ _internal_send(ns, c);
816816+ }
817817+ }
818818+ }
819819+ }
820820+ _nc_table_traverse_end(ns->client_table, tt);
821821+822822+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
823823+}
824824+825825+void
826826+_notify_lib_suspend_port(notify_state_t *ns, mach_port_t port)
827827+{
828828+ portproc_data_t *pdata;
829829+830830+ if (ns == NULL) return;
831831+832832+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
833833+834834+ pdata = _nc_table_find_n(ns->port_table, port);
835835+ if (pdata != NULL) pdata->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED;
836836+837837+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
838838+}
839839+840840+void
841841+_notify_lib_resume_port(notify_state_t *ns, mach_port_t port)
842842+{
843843+ client_t *c;
844844+ void *tt;
845845+ portproc_data_t *pdata;
846846+847847+ if (ns == NULL) return;
848848+849849+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
850850+851851+ /* Resume all subscriptions with this port */
852852+ pdata = _nc_table_find_n(ns->port_table, port);
853853+ if (pdata != NULL) pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED;
854854+855855+ tt = _nc_table_traverse_start(ns->client_table);
856856+ while (tt != NULL)
857857+ {
858858+ c = _nc_table_traverse(ns->client_table, tt);
859859+ if (c == NULL) break;
860860+861861+ if (c->port == port)
862862+ {
863863+ if (c->suspend_count > 0) c->suspend_count--;
864864+ if (c->suspend_count == 0)
865865+ {
866866+ c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED;
867867+ c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
868868+869869+ if (c->state & NOTIFY_CLIENT_STATE_PENDING)
870870+ {
871871+ _internal_send(ns, c);
872872+ }
873873+ }
874874+ }
875875+ }
876876+ _nc_table_traverse_end(ns->client_table, tt);
877877+878878+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
879879+}
880880+881881+/*
882882+ * Delete all clients for a process
883883+ * N.B. notifyd does not use this routine.
884884+ */
885885+void
886886+_notify_lib_cancel_proc(notify_state_t *ns, pid_t pid)
887887+{
888888+ client_t *c;
889889+ void *tt;
890890+ list_t *l, *x;
891891+892892+ if (ns == NULL) return;
893893+894894+ x = NULL;
895895+896896+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
897897+898898+ tt = _nc_table_traverse_start(ns->client_table);
899899+ while (tt != NULL)
900900+ {
901901+ c = _nc_table_traverse(ns->client_table, tt);
902902+ if (c == NULL) break;
903903+904904+ if (c->pid == pid) x = _nc_list_prepend(x, _nc_list_new(c));
905905+ }
906906+ _nc_table_traverse_end(ns->client_table, tt);
907907+908908+ for (l = x; l != NULL; l = _nc_list_next(l))
909909+ {
910910+ c = _nc_list_data(l);
911911+ _internal_cancel(ns, c->client_id);
912912+ }
913913+914914+ _nc_list_release_list(x);
915915+916916+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
917917+}
918918+919919+/*
920920+ * Delete all clients for a port
921921+ * N.B. notifyd does not use this routine.
922922+ */
923923+void
924924+_notify_lib_cancel_port(notify_state_t *ns, mach_port_t port)
925925+{
926926+ client_t *c;
927927+ void *tt;
928928+ list_t *l, *x;
929929+930930+ if (ns == NULL) return;
931931+932932+ x = NULL;
933933+934934+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
935935+936936+ tt = _nc_table_traverse_start(ns->client_table);
937937+ while (tt != NULL)
938938+ {
939939+ c = _nc_table_traverse(ns->client_table, tt);
940940+ if (c == NULL) break;
941941+942942+ if (c->port == port) x = _nc_list_prepend(x, _nc_list_new(c));
943943+ }
944944+ _nc_table_traverse_end(ns->client_table, tt);
945945+946946+ for (l = x; l != NULL; l = _nc_list_next(l))
947947+ {
948948+ c = _nc_list_data(l);
949949+ _internal_cancel(ns, c->client_id);
950950+ }
951951+952952+ _nc_list_release_list(x);
953953+954954+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
955955+}
956956+957957+/*
958958+ * Check if a name has changed since the last time this client checked.
959959+ * Returns true, false, or error.
960960+ */
961961+uint32_t
962962+_notify_lib_check(notify_state_t *ns, pid_t pid, int token, int *check)
963963+{
964964+ client_t *c;
965965+ uint64_t cid;
966966+967967+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
968968+ if (check == NULL) return NOTIFY_STATUS_FAILED;
969969+970970+ cid = make_client_id(pid, token);
971971+972972+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
973973+974974+ c = _nc_table_find_64(ns->client_table, cid);
975975+976976+ if (c == NULL)
977977+ {
978978+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
979979+ return NOTIFY_STATUS_INVALID_TOKEN;
980980+ }
981981+982982+ if (c->name_info == NULL)
983983+ {
984984+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
985985+ return NOTIFY_STATUS_INVALID_TOKEN;
986986+ }
987987+988988+ if (c->name_info->val == c->lastval)
989989+ {
990990+ *check = 0;
991991+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
992992+ return NOTIFY_STATUS_OK;
993993+ }
994994+995995+ c->lastval = c->name_info->val;
996996+ *check = 1;
997997+998998+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
999999+ return NOTIFY_STATUS_OK;
10001000+}
10011001+10021002+/*
10031003+ * SPI: get value for a name.
10041004+ */
10051005+uint32_t
10061006+_notify_lib_peek(notify_state_t *ns, pid_t pid, int token, int *val)
10071007+{
10081008+ client_t *c;
10091009+ uint64_t cid;
10101010+10111011+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
10121012+ if (val == NULL) return NOTIFY_STATUS_FAILED;
10131013+10141014+ cid = make_client_id(pid, token);
10151015+10161016+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
10171017+10181018+ c = _nc_table_find_64(ns->client_table, cid);
10191019+10201020+ if (c == NULL)
10211021+ {
10221022+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
10231023+ return NOTIFY_STATUS_INVALID_TOKEN;
10241024+ }
10251025+10261026+ if (c->name_info == NULL)
10271027+ {
10281028+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
10291029+ return NOTIFY_STATUS_INVALID_TOKEN;
10301030+ }
10311031+10321032+ *val = c->name_info->val;
10331033+10341034+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
10351035+ return NOTIFY_STATUS_OK;
10361036+}
10371037+10381038+int *
10391039+_notify_lib_check_addr(notify_state_t *ns, pid_t pid, int token)
10401040+{
10411041+ client_t *c;
10421042+ int *addr;
10431043+ uint64_t cid;
10441044+10451045+ if (ns == NULL) return NULL;
10461046+10471047+ cid = make_client_id(pid, token);
10481048+10491049+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
10501050+10511051+ c = _nc_table_find_64(ns->client_table, cid);
10521052+10531053+ if (c == NULL)
10541054+ {
10551055+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
10561056+ return NULL;
10571057+ }
10581058+10591059+ if (c->name_info == NULL)
10601060+ {
10611061+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
10621062+ return NULL;
10631063+ }
10641064+10651065+ addr = (int *)&(c->name_info->val);
10661066+10671067+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
10681068+ return addr;
10691069+}
10701070+10711071+/*
10721072+ * Get state value for a name.
10731073+ */
10741074+uint32_t
10751075+_notify_lib_get_state(notify_state_t *ns, uint64_t nid, uint64_t *state, uid_t uid, gid_t gid)
10761076+{
10771077+ name_info_t *n;
10781078+10791079+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
10801080+ if (state == NULL) return NOTIFY_STATUS_FAILED;
10811081+10821082+ *state = 0;
10831083+10841084+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
10851085+10861086+ n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid);
10871087+10881088+ if (n == NULL)
10891089+ {
10901090+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
10911091+ return NOTIFY_STATUS_INVALID_NAME;
10921092+ }
10931093+10941094+#ifdef GET_STATE_AUTH_CHECK
10951095+ int auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_READ);
10961096+ if (auth != 0)
10971097+ {
10981098+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
10991099+ return NOTIFY_STATUS_NOT_AUTHORIZED;
11001100+ }
11011101+#endif
11021102+11031103+ *state = n->state;
11041104+11051105+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
11061106+ return NOTIFY_STATUS_OK;
11071107+}
11081108+11091109+/*
11101110+ * Set state value for a name.
11111111+ */
11121112+uint32_t
11131113+_notify_lib_set_state(notify_state_t *ns, uint64_t nid, uint64_t state, uid_t uid, gid_t gid)
11141114+{
11151115+ name_info_t *n;
11161116+ int auth;
11171117+11181118+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
11191119+11201120+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
11211121+11221122+ n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid);
11231123+11241124+ if (n == NULL)
11251125+ {
11261126+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
11271127+ return NOTIFY_STATUS_INVALID_NAME;
11281128+ }
11291129+11301130+ auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_WRITE);
11311131+ if (auth != 0)
11321132+ {
11331133+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
11341134+ return NOTIFY_STATUS_NOT_AUTHORIZED;
11351135+ }
11361136+11371137+ n->state = state;
11381138+ n->state_time = mach_absolute_time();
11391139+11401140+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
11411141+ return NOTIFY_STATUS_OK;
11421142+}
11431143+11441144+static uint32_t
11451145+_internal_register_common(notify_state_t *ns, const char *name, pid_t pid, int token, uid_t uid, gid_t gid, client_t **outc)
11461146+{
11471147+ client_t *c;
11481148+ name_info_t *n;
11491149+ int is_new_name;
11501150+ uint32_t status;
11511151+11521152+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
11531153+ if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
11541154+ if (outc == NULL) return NOTIFY_STATUS_OK;
11551155+11561156+ status = _internal_check_access(ns, name, uid, gid, NOTIFY_ACCESS_READ);
11571157+ if (status != NOTIFY_STATUS_OK) return NOTIFY_STATUS_NOT_AUTHORIZED;
11581158+11591159+ *outc = NULL;
11601160+ is_new_name = 0;
11611161+11621162+ n = (name_info_t *)_nc_table_find(ns->name_table, name);
11631163+ if (n == NULL)
11641164+ {
11651165+ is_new_name = 1;
11661166+11671167+ n = _internal_new_name(ns, name);
11681168+ if (n == NULL) return NOTIFY_STATUS_FAILED;
11691169+ }
11701170+11711171+ c = _internal_client_new(ns, pid, token);
11721172+ if (c == NULL)
11731173+ {
11741174+ if (is_new_name == 1)
11751175+ {
11761176+ _nc_table_delete(ns->name_table, n->name);
11771177+ _nc_list_release_list(n->subscriptions);
11781178+ free(n);
11791179+ ns->stat_name_free++;
11801180+ }
11811181+11821182+ return NOTIFY_STATUS_FAILED;
11831183+ }
11841184+11851185+ n->refcount++;
11861186+11871187+ c->name_info = n;
11881188+ n->subscriptions = _nc_list_prepend(n->subscriptions, _nc_list_new(c));
11891189+11901190+ *outc = c;
11911191+11921192+ return NOTIFY_STATUS_OK;
11931193+}
11941194+11951195+/*
11961196+ * Register for signal.
11971197+ * Returns the client_id;
11981198+ */
11991199+uint32_t
12001200+_notify_lib_register_signal(notify_state_t *ns, const char *name, pid_t pid, int token, uint32_t sig, uid_t uid, gid_t gid, uint64_t *out_nid)
12011201+{
12021202+ client_t *c;
12031203+ uint32_t status;
12041204+12051205+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
12061206+ if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
12071207+12081208+ c = NULL;
12091209+12101210+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
12111211+12121212+ status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
12131213+ if (status != NOTIFY_STATUS_OK)
12141214+ {
12151215+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
12161216+ return status;
12171217+ }
12181218+12191219+ c->notify_type = NOTIFY_TYPE_SIGNAL;
12201220+ c->pid = pid;
12211221+ c->sig = sig;
12221222+ *out_nid = c->name_info->name_id;
12231223+12241224+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
12251225+ return NOTIFY_STATUS_OK;
12261226+}
12271227+12281228+/*
12291229+ * Register for notification on a file descriptor.
12301230+ * Returns the client_id;
12311231+ */
12321232+uint32_t
12331233+_notify_lib_register_file_descriptor(notify_state_t *ns, const char *name, pid_t pid, int token, int fd, uid_t uid, gid_t gid, uint64_t *out_nid)
12341234+{
12351235+ client_t *c;
12361236+ uint32_t status;
12371237+12381238+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
12391239+ if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
12401240+12411241+ c = NULL;
12421242+12431243+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
12441244+12451245+ status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
12461246+ if (status != NOTIFY_STATUS_OK)
12471247+ {
12481248+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
12491249+ return status;
12501250+ }
12511251+12521252+ c->notify_type = NOTIFY_TYPE_FILE;
12531253+ c->fd = fd;
12541254+ *out_nid = c->name_info->name_id;
12551255+12561256+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
12571257+ return NOTIFY_STATUS_OK;
12581258+}
12591259+12601260+/*
12611261+ * Register for notification on a mach port.
12621262+ * Returns the client_id;
12631263+ */
12641264+uint32_t
12651265+_notify_lib_register_mach_port(notify_state_t *ns, const char *name, pid_t pid, int token, mach_port_t port, uid_t uid, gid_t gid, uint64_t *out_nid)
12661266+{
12671267+ client_t *c;
12681268+ uint32_t status;
12691269+12701270+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
12711271+ if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
12721272+12731273+ c = NULL;
12741274+12751275+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
12761276+12771277+ status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
12781278+ if (status != NOTIFY_STATUS_OK)
12791279+ {
12801280+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
12811281+ return status;
12821282+ }
12831283+12841284+ c->notify_type = NOTIFY_TYPE_PORT;
12851285+ c->port = port;
12861286+ *out_nid = c->name_info->name_id;
12871287+12881288+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
12891289+ return NOTIFY_STATUS_OK;
12901290+}
12911291+12921292+/*
12931293+ * Plain registration - only for notify_check()
12941294+ * Returns the client_id.
12951295+ */
12961296+uint32_t
12971297+_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)
12981298+{
12991299+ client_t *c;
13001300+ uint32_t status;
13011301+13021302+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
13031303+ if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
13041304+13051305+ c = NULL;
13061306+13071307+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
13081308+13091309+ status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
13101310+ if (status != NOTIFY_STATUS_OK)
13111311+ {
13121312+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
13131313+ return status;
13141314+ }
13151315+13161316+ if (slot == SLOT_NONE)
13171317+ {
13181318+ c->notify_type = NOTIFY_TYPE_PLAIN;
13191319+ }
13201320+ else
13211321+ {
13221322+ c->notify_type = NOTIFY_TYPE_MEMORY;
13231323+ c->name_info->slot = slot;
13241324+ }
13251325+13261326+ *out_nid = c->name_info->name_id;
13271327+13281328+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
13291329+ return NOTIFY_STATUS_OK;
13301330+}
13311331+13321332+uint32_t
13331333+_notify_lib_set_owner(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
13341334+{
13351335+ name_info_t *n;
13361336+13371337+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
13381338+ if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
13391339+13401340+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
13411341+13421342+ n = (name_info_t *)_nc_table_find(ns->name_table, name);
13431343+ if (n == NULL)
13441344+ {
13451345+ /* create new name */
13461346+ n = _internal_new_name(ns, name);
13471347+ if (n == NULL)
13481348+ {
13491349+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
13501350+ return NOTIFY_STATUS_FAILED;
13511351+ }
13521352+13531353+ /*
13541354+ * Setting the refcount here allows the namespace to be "pre-populated"
13551355+ * with controlled names. notifyd does this for reserved names in
13561356+ * its configuration file.
13571357+ */
13581358+ n->refcount++;
13591359+ }
13601360+13611361+ n->uid = uid;
13621362+ n->gid = gid;
13631363+13641364+ _internal_insert_controlled_name(ns, n);
13651365+13661366+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
13671367+ return NOTIFY_STATUS_OK;
13681368+}
13691369+13701370+uint32_t
13711371+_notify_lib_get_owner(notify_state_t *ns, const char *name, uint32_t *uid, uint32_t *gid)
13721372+{
13731373+ name_info_t *n;
13741374+ int i, nlen, len;
13751375+13761376+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
13771377+ if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
13781378+13791379+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
13801380+13811381+ n = (name_info_t *)_nc_table_find(ns->name_table, name);
13821382+ if (n != NULL)
13831383+ {
13841384+ *uid = n->uid;
13851385+ *gid = n->gid;
13861386+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
13871387+ return NOTIFY_STATUS_OK;
13881388+ }
13891389+13901390+ len = strlen(name);
13911391+13921392+ for (i = 0; i < ns->controlled_name_count; i++)
13931393+ {
13941394+ n = ns->controlled_name[i];
13951395+ if (n == NULL) break;
13961396+13971397+ nlen = strlen(n->name);
13981398+13991399+ if (!strcmp(n->name, name))
14001400+ {
14011401+ *uid = n->uid;
14021402+ *gid = n->gid;
14031403+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
14041404+ return NOTIFY_STATUS_OK;
14051405+ }
14061406+14071407+ /* check if this key is a prefix */
14081408+ if (nlen >= len) continue;
14091409+ if (strncmp(n->name, name, nlen)) continue;
14101410+14111411+ *uid = n->uid;
14121412+ *gid = n->gid;
14131413+14141414+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
14151415+ return NOTIFY_STATUS_OK;
14161416+ }
14171417+14181418+ *uid = 0;
14191419+ *gid = 0;
14201420+14211421+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
14221422+ return NOTIFY_STATUS_OK;
14231423+}
14241424+14251425+uint32_t
14261426+_notify_lib_set_access(notify_state_t *ns, const char *name, uint32_t mode)
14271427+{
14281428+ name_info_t *n;
14291429+14301430+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
14311431+ if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
14321432+14331433+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
14341434+14351435+ n = (name_info_t *)_nc_table_find(ns->name_table, name);
14361436+ if (n == NULL)
14371437+ {
14381438+ /* create new name */
14391439+ n = _internal_new_name(ns, name);
14401440+ if (n == NULL)
14411441+ {
14421442+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
14431443+ return NOTIFY_STATUS_FAILED;
14441444+ }
14451445+14461446+ /*
14471447+ * Setting the refcount here allows the namespace to be "pre-populated"
14481448+ * with controlled names. notifyd does this for reserved names in
14491449+ * its configuration file.
14501450+ */
14511451+ n->refcount++;
14521452+ }
14531453+14541454+ n->access = mode;
14551455+14561456+ _internal_insert_controlled_name(ns, n);
14571457+14581458+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
14591459+ return NOTIFY_STATUS_OK;
14601460+}
14611461+14621462+uint32_t
14631463+_notify_lib_get_access(notify_state_t *ns, const char *name, uint32_t *mode)
14641464+{
14651465+ name_info_t *n;
14661466+ int i, nlen, len;
14671467+14681468+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
14691469+ if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
14701470+14711471+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
14721472+14731473+ n = (name_info_t *)_nc_table_find(ns->name_table, name);
14741474+ if (n != NULL)
14751475+ {
14761476+ *mode = n->access;
14771477+14781478+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
14791479+ return NOTIFY_STATUS_OK;
14801480+ }
14811481+14821482+ len = strlen(name);
14831483+14841484+ for (i = 0; i < ns->controlled_name_count; i++)
14851485+ {
14861486+ n = ns->controlled_name[i];
14871487+ if (n == NULL) break;
14881488+ if (n->name == NULL) continue;
14891489+14901490+ nlen = strlen(n->name);
14911491+14921492+ if (!strcmp(n->name, name))
14931493+ {
14941494+ *mode = n->access;
14951495+14961496+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
14971497+ return NOTIFY_STATUS_OK;
14981498+ }
14991499+15001500+ /* check if this key is a prefix */
15011501+ if (nlen >= len) continue;
15021502+ if (strncmp(n->name, name, nlen)) continue;
15031503+15041504+ *mode = n->access;
15051505+15061506+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
15071507+ return NOTIFY_STATUS_OK;
15081508+ }
15091509+15101510+ *mode = NOTIFY_ACCESS_DEFAULT;
15111511+15121512+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
15131513+ return NOTIFY_STATUS_OK;
15141514+}
15151515+15161516+uint32_t
15171517+_notify_lib_release_name(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
15181518+{
15191519+ name_info_t *n;
15201520+15211521+ if (ns == NULL) return NOTIFY_STATUS_FAILED;
15221522+ if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
15231523+15241524+ if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
15251525+15261526+ n = (name_info_t *)_nc_table_find(ns->name_table, name);
15271527+ if (n == NULL)
15281528+ {
15291529+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
15301530+ return NOTIFY_STATUS_INVALID_NAME;
15311531+ }
15321532+15331533+ /* Owner and root may release */
15341534+ if ((n->uid != uid) && (uid != 0))
15351535+ {
15361536+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
15371537+ return NOTIFY_STATUS_NOT_AUTHORIZED;
15381538+ }
15391539+15401540+ _internal_release_name_info(ns, n);
15411541+15421542+ if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
15431543+ return NOTIFY_STATUS_OK;
15441544+}
+216
libnotify/libnotify.h
···11+/*
22+ * Copyright (c) 2003-2012 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#ifndef _LIBNOTIFY_H_
2525+#define _LIBNOTIFY_H_
2626+2727+#include <pthread.h>
2828+#include <mach/mach.h>
2929+#include <dispatch/dispatch.h>
3030+#include "table.h"
3131+3232+#include <TargetConditionals.h>
3333+3434+#if TARGET_IPHONE_SIMULATOR
3535+extern const char *_notify_shm_id();
3636+#define SHM_ID _notify_shm_id()
3737+#else
3838+#define SHM_ID "apple.shm.notification_center"
3939+#endif
4040+4141+#define NOTIFY_IPC_VERSION_NAME "com.apple.system.notify.ipc_version"
4242+#define NOTIFY_IPC_VERSION_NAME_LEN 35
4343+#define NOTIFY_SERVICE_NAME "com.apple.system.notification_center"
4444+#define NOTIFY_SERVICE_NAME_LEN 36
4545+4646+#define COMMON_PORT_KEY "com.apple.system.notify.common"
4747+4848+/* Notification types */
4949+#define NOTIFY_TYPE_NONE 0x00000000
5050+#define NOTIFY_TYPE_MEMORY 0x00000001
5151+#define NOTIFY_TYPE_PLAIN 0x00000002
5252+#define NOTIFY_TYPE_PORT 0x00000004
5353+#define NOTIFY_TYPE_FILE 0x00000008
5454+#define NOTIFY_TYPE_SIGNAL 0x00000010
5555+#define NOTIFY_TYPE_MASK 0x000000ff
5656+#define NOTIFY_FLAG_SELF 0x80000000
5757+#define NOTIFY_FLAG_REGEN 0x40000000
5858+#define NOTIFY_FLAG_RELEASE_SEND 0x20000000
5959+6060+/* Return values for notify_check() */
6161+#define NOTIFY_CHECK_FALSE 0
6262+#define NOTIFY_CHECK_TRUE 1
6363+#define NOTIFY_CHECK_ERROR 2
6464+6565+/* Access control */
6666+#define NOTIFY_ACCESS_READ 1
6767+#define NOTIFY_ACCESS_WRITE 2
6868+6969+#define NOTIFY_ACCESS_OTHER_SHIFT 8
7070+#define NOTIFY_ACCESS_GROUP_SHIFT 4
7171+#define NOTIFY_ACCESS_USER_SHIFT 0
7272+7373+#define NOTIFY_ACCESS_DEFAULT 0x00000333
7474+#define NOTIFY_ACCESS_USER_RW 0x00000003
7575+7676+/* Filesystem Services */
7777+#define NOTIFY_SERVICE_FILE_STATUS_QUO 0x00
7878+#define NOTIFY_SERVICE_FILE_ADD 0x01
7979+#define NOTIFY_SERVICE_FILE_DELETE 0x02
8080+#define NOTIFY_SERVICE_FILE_MODIFY 0x04
8181+#define NOTIFY_SERVICE_FILE_ATTR 0x08
8282+8383+#define NOTIFY_SERVICE_DIR_FILE_ADD 0x10
8484+#define NOTIFY_SERVICE_DIR_FILE_DELETE 0x20
8585+8686+#define NOTIFY_CLIENT_STATE_SUSPENDED 0x00000001
8787+#define NOTIFY_CLIENT_STATE_PENDING 0x00000002
8888+#define NOTIFY_CLIENT_STATE_TIMEOUT 0x00000004
8989+9090+#define NOTIFY_PORT_PROC_TYPE_PORT 0x00000010
9191+#define NOTIFY_PORT_PROC_TYPE_PROC 0x00000020
9292+#define NOTIFY_PORT_PROC_TYPE_MASK 0x000000f0
9393+#define NOTIFY_PORT_PROC_STATE_INVALID 0x00000001
9494+#define NOTIFY_PORT_PROC_STATE_SUSPENDED 0x00000002
9595+#define NOTIFY_PORT_PROC_STATE_MASK 0x0000000f
9696+9797+/* notify state flags */
9898+#define NOTIFY_STATE_USE_LOCKS 0x00000001
9999+#define NOTIFY_STATE_ENABLE_RESEND 0x00000002
100100+101101+#define NOTIFY_CLIENT_SELF 0
102102+#define SIGNAL_NONE -1
103103+#define FD_NONE -1
104104+#define SLOT_NONE -1
105105+106106+#define _notify_lib_port_new(A,B,C,D) _notify_lib_port_proc_new(A,B,0,C,D)
107107+#define _notify_lib_proc_new(A,B,C,D) _notify_lib_port_proc_new(A,MACH_PORT_NULL,B,C,D)
108108+#define _notify_lib_port_find(A,B) _notify_lib_port_proc_find(A,B,0)
109109+#define _notify_lib_proc_find(A,B) _notify_lib_port_proc_find(A,MACH_PORT_NULL,B)
110110+#define _notify_lib_port_release(A,B) _notify_lib_port_proc_release(A,B,0)
111111+#define _notify_lib_proc_release(A,B) _notify_lib_port_proc_release(A,MACH_PORT_NULL,B)
112112+113113+typedef struct
114114+{
115115+ char *name;
116116+ uint64_t name_id;
117117+ uint32_t uid;
118118+ uint32_t gid;
119119+ uint32_t access;
120120+ uint32_t slot;
121121+ uint32_t refcount;
122122+ uint32_t val;
123123+ uint64_t state;
124124+ uint64_t state_time;
125125+ void *private;
126126+ list_t *subscriptions;
127127+} name_info_t;
128128+129129+typedef struct
130130+{
131131+ uint64_t client_id;
132132+ uint32_t state;
133133+ name_info_t *name_info;
134134+ uint32_t suspend_count;
135135+ uint32_t notify_type;
136136+ uint32_t lastval;
137137+ mach_port_t port;
138138+ int fd;
139139+ uint32_t send_val;
140140+ uint32_t pid;
141141+ uint32_t sig;
142142+ void *private;
143143+} client_t;
144144+145145+typedef struct
146146+{
147147+ uint32_t refcount;
148148+ uint32_t flags;
149149+ dispatch_source_t src;
150150+} portproc_data_t;
151151+152152+typedef struct
153153+{
154154+ uint32_t flags;
155155+ table_t *name_table;
156156+ table_t *name_id_table;
157157+ table_t *client_table;
158158+ table_t *port_table;
159159+ table_t *proc_table;
160160+ name_info_t **controlled_name;
161161+ uint32_t controlled_name_count;
162162+ pthread_mutex_t *lock;
163163+ int sock;
164164+ uint32_t stat_name_alloc;
165165+ uint32_t stat_name_free;
166166+ uint32_t stat_client_alloc;
167167+ uint32_t stat_client_free;
168168+ uint32_t stat_portproc_alloc;
169169+ uint32_t stat_portproc_free;
170170+} notify_state_t;
171171+172172+notify_state_t *_notify_lib_notify_state_new(uint32_t flags, uint32_t table_size);
173173+void _notify_lib_notify_state_free(notify_state_t *ns);
174174+175175+uint32_t _notify_lib_post(notify_state_t *ns, const char *name, uint32_t uid, uint32_t gid);
176176+uint32_t _notify_lib_post_nid(notify_state_t *ns, uint64_t nid, uid_t uid, gid_t gid);
177177+uint32_t _notify_lib_post_client(notify_state_t *ns, client_t *c);
178178+179179+uint32_t _notify_lib_check(notify_state_t *ns, pid_t pid, int token, int *check);
180180+uint32_t _notify_lib_get_state(notify_state_t *ns, uint64_t nid, uint64_t *state, uint32_t uid, uint32_t gid);
181181+uint32_t _notify_lib_set_state(notify_state_t *ns, uint64_t nid, uint64_t state, uint32_t uid, uint32_t gid);
182182+183183+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);
184184+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);
185185+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);
186186+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);
187187+188188+uint32_t _notify_lib_get_owner(notify_state_t *ns, const char *name, uint32_t *uid, uint32_t *gid);
189189+uint32_t _notify_lib_get_access(notify_state_t *ns, const char *name, uint32_t *access);
190190+191191+uint32_t _notify_lib_set_owner(notify_state_t *ns, const char *name, uint32_t uid, uint32_t gid);
192192+uint32_t _notify_lib_set_access(notify_state_t *ns, const char *name, uint32_t access);
193193+194194+uint32_t _notify_lib_release_name(notify_state_t *ns, const char *name, uint32_t uid, uint32_t gid);
195195+196196+void _notify_lib_cancel(notify_state_t *ns, pid_t pid, int token);
197197+void _notify_lib_suspend(notify_state_t *ns, pid_t pid, int token);
198198+uint32_t _notify_lib_resume(notify_state_t *ns, pid_t pid, int token);
199199+200200+void _notify_lib_cancel_proc(notify_state_t *ns, pid_t pid);
201201+void _notify_lib_suspend_proc(notify_state_t *ns, pid_t pid);
202202+void _notify_lib_resume_proc(notify_state_t *ns, pid_t pid);
203203+204204+void _notify_lib_suspend_port(notify_state_t *ns, mach_port_t port);
205205+void _notify_lib_resume_port(notify_state_t *ns, mach_port_t port);
206206+207207+uint32_t _notify_lib_check_controlled_access(notify_state_t *ns, char *name, uid_t uid, gid_t gid, int req);
208208+209209+uint64_t make_client_id(pid_t pid, int token);
210210+211211+uint32_t _notify_lib_port_proc_new(notify_state_t *ns, mach_port_t port, pid_t proc, uint32_t state, dispatch_source_t src);
212212+portproc_data_t *_notify_lib_port_proc_find(notify_state_t *ns, mach_port_t port, pid_t proc);
213213+void _notify_lib_port_proc_release(notify_state_t *ns, mach_port_t port, pid_t proc);
214214+215215+216216+#endif /* _LIBNOTIFY_H_ */
+419
libnotify/notify.3
···11+.\" Copyright (c) 2003-2014 Apple Inc. All rights reserved.
22+.\"
33+.\" @APPLE_LICENSE_HEADER_START@
44+.\"
55+.\" Portions Copyright (c) 2003-2010 Apple Inc. All Rights Reserved.
66+.\"
77+.\" This file contains Original Code and/or Modifications of Original Code
88+.\" as defined in and that are subject to the Apple Public Source License
99+.\" Version 2.0 (the 'License'). You may not use this file except in
1010+.\" compliance with the License. Please obtain a copy of the License at
1111+.\" http://www.opensource.apple.com/apsl/ and read it before using this
1212+.\" file.
1313+.\"
1414+.\" The Original Code and all software distributed under the License are
1515+.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1616+.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1717+.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1818+.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1919+.\" Please see the License for the specific language governing rights and
2020+.\" limitations under the License.
2121+.\"
2222+.\" @APPLE_LICENSE_HEADER_END@
2323+.\"
2424+.\"
2525+.Dd September 3, 2008
2626+.Dt notify 3
2727+.Os "Mac OS X"
2828+.Sh NAME
2929+.Nm notify_post ,
3030+.Nm notify_register_check ,
3131+.Nm notify_register_dispatch ,
3232+.Nm notify_register_signal ,
3333+.Nm notify_register_mach_port ,
3434+.Nm notify_register_file_descriptor ,
3535+.Nm notify_check ,
3636+.Nm notify_get_state ,
3737+.Nm notify_set_state ,
3838+.Nm notify_suspend ,
3939+.Nm notify_resume ,
4040+.Nm notify_cancel ,
4141+.Nm notify_is_valid_token
4242+.Nd event distribution functions
4343+.Sh SYNOPSIS
4444+.Fd #include <notify.h>
4545+.Ft uint32_t
4646+.Fn notify_post "const char *name"
4747+.Ft uint32_t
4848+.Fn notify_register_check "const char *name, int *out_token"
4949+.Ft uint32_t
5050+.Fn notify_register_dispatch "const char *name, int *out_token" "dispatch_queue_t queue" "notify_handler_t handler"
5151+.Ft uint32_t
5252+.Fn notify_register_signal "const char *name, int sig, int *out_token"
5353+.Ft uint32_t
5454+.Fn notify_register_mach_port "const char *name, mach_port_t *notify_port, int flags, int *out_token"
5555+.Ft uint32_t
5656+.Fn notify_register_file_descriptor "const char *name, int *notify_fd, int flags, int *out_token"
5757+.Ft uint32_t
5858+.Fn notify_check "int token, int *check"
5959+.Ft uint32_t
6060+.Fn notify_set_state "int token, uint64_t state"
6161+.Ft uint32_t
6262+.Fn notify_get_state "int token, uint64_t *state"
6363+.Ft uint32_t
6464+.Fn notify_suspend "int token"
6565+.Ft uint32_t
6666+.Fn notify_resume "int token"
6767+.Ft uint32_t
6868+.Fn notify_cancel "int token"
6969+.Ft bool
7070+.Fn notify_is_valid_token "int val"
7171+.Sh DESCRIPTION
7272+These routines allow processes to exchange stateless notification events.
7373+Processes post notifications to a single system-wide notification server,
7474+which then distributes notifications to client processes that have
7575+registered to receive those notifications, including processes run by
7676+other users.
7777+.Pp
7878+Notifications are associated with names in a namespace shared by all
7979+clients of the system.
8080+Clients may post notifications for names, and
8181+may monitor names for posted notifications.
8282+Clients may request
8383+notification delivery by a number of different methods.
8484+.Pp
8585+Clients desiring to monitor names in the notification system must
8686+register with the system, providing a name and other information
8787+required for the desired notification delivery method.
8888+Clients are
8989+given an integer token representing the registration.
9090+Token values are zero or positive integers.
9191+.Pp
9292+The kernel provides limited queues for mach message and file descriptor messages.
9393+It is important to make sure that clients read mach ports and file descriptors frequently
9494+to prevent messages from being lost due to resource limitations.
9595+Clients that use signal-based notification should be aware that signals
9696+are not delivered to a process while it is running in a signal handler.
9797+This may affect the delivery of signals in close succession.
9898+.Pp
9999+Notifications may be coalesced in some cases.
100100+Multiple events posted
101101+for a name in rapid succession may result in a single notification sent
102102+to clients registered for notification for that name.
103103+Clients checking
104104+for changes using the notify_check() routine cannot determine if
105105+more than one event has been posted since a previous call to
106106+notify_check() for that name.
107107+.Pp
108108+"False positives" may occur in notify_check() when used with a token
109109+generated by notify_register_check() due to implementation constraints.
110110+This behavior may vary in future releases.
111111+.Ss notify_post
112112+This routine causes the system to send a notification for the given
113113+name to all clients that have registered for notifications of this name.
114114+This is the only API required for an application that only produces
115115+notifications.
116116+.Ss notify_register_check
117117+Registers for passive notification for the given name.
118118+The routine generates
119119+a token that may be used with the
120120+.Fn notify_check
121121+routine to check if any notifications have been posted for the name.
122122+The check is implemented using a shared memory scheme, making the check
123123+very fast and efficient.
124124+The implementation has a limited amount
125125+of shared memory, so developers are encouraged to use this mechanism
126126+sparingly.
127127+It is also important to release the resources consumed
128128+by a registration with
129129+.Fn notify_cancel
130130+when they are no longer required by the application.
131131+.Ss notify_register_dispatch
132132+registers a callback handler in the form of a block which will be
133133+dispatched to the queue when a notification for the given name is
134134+received. This is a convenient way to register callbacks without any
135135+management of file descriptors, mach ports, or signals on the part of
136136+the application. The given queue is retained by the system for the
137137+lifetime of the notification. Use
138138+.Fn notify_cancel
139139+to release the notification and its reference to the queue.
140140+.Ss notify_register_signal
141141+registers a client for notification delivery via a signal.
142142+This fits
143143+well with the design of many UNIX daemons that use a signal such as SIGHUP
144144+to reinitialize of reset internal state information.
145145+Clients may use the
146146+registration token generated by this routine to check for notifications using
147147+.Fn notify_check .
148148+This allows the application to determine if a signal was received as the
149149+result of a notification, or if the signal was generated by some other source.
150150+It also permits the application that registers for signal notification for
151151+multiple names to determine which name was associated with the notification.
152152+.Ss notify_register_mach_port
153153+registers a client for notification delivery via mach messaging.
154154+Notifications are delivered by an empty message sent to a mach port.
155155+By default, a new port is created by a call to this routine.
156156+A mach port
157157+previously created by a call to this routine may be used for notifications
158158+if a pointer to that port is passed in to the routine and NOTIFY_REUSE is
159159+set in the flags parameter.
160160+The notification service must be able to extract
161161+send rights to the port.
162162+.Pp
163163+Note that the kernel limits the size of the message queue for any port.
164164+If it is important that notifications should not be lost due to queue
165165+overflow, clients should service messages quickly, and be cautious in
166166+using the same port for notifications for more than one name.
167167+.Pp
168168+A notification message has an empty message body.
169169+The msgh_id field
170170+in the mach message header will have the value of the notification
171171+token.
172172+If a port is reused for multiple notification registrations,
173173+the msgh_id value may be used to determine which name generated
174174+the notification.
175175+.Ss notify_register_file_descriptor
176176+Register for notification by a write to a file descriptor.
177177+.Pp
178178+By default, a new file descriptor is created and a pointer to it
179179+is returned as the value of the "notify_fd" parameter.
180180+A file descriptor
181181+created by a previous call to this routine may be used for notifications
182182+if a pointer to that file descriptor is passed in to the routine and
183183+NOTIFY_REUSE is set in the flags parameter.
184184+.Pp
185185+Note that the kernel limits the buffer space for queued writes on a
186186+file descriptor.
187187+If it is important that notifications should not be
188188+lost due to queue overflow, clients should service messages quickly,
189189+and be cautious in using the same file descriptor for notifications
190190+for more than one name.
191191+.Pp
192192+Notifications are delivered by an integer value written to the
193193+file descriptor.
194194+The value is sent in network byte order.
195195+When converted to host byte order, for example by using
196196+.Fn ntohl ,
197197+it will match the notification token
198198+for which the notification was generated.
199199+.Ss notify_check
200200+Checks if any notifications have been posted for a name.
201201+The output
202202+parameter "check" is set to 0 for false, 1 for true.
203203+A true indication is
204204+returned the first time notify_check is called for a token.
205205+Subsequent calls
206206+give a true indication when notifications have been posted for the name
207207+associated with the notification token.
208208+.Pp
209209+.Fn notify_check
210210+may be used with any notification token produced by any of the notification
211211+registration routines.
212212+A fast check based on a shared memory implementation
213213+is used when the token was generated by
214214+.Fn notify_register_check .
215215+Other tokens are checked by a call to the notification server.
216216+.Ss notify_set_state
217217+Set a 64-bit unsigned integer variable associated with a token.
218218+.Pp
219219+Each registered notification key has an associated 64-bit integer variable,
220220+which may be set using this routine and examined using the
221221+.Fn notify_get_state
222222+routine.
223223+The state variable is free to be used by clients of the notification API.
224224+It may be used to synchronize state information between cooperating processes or threads.
225225+(Available in Mac OS X 10.5 or later.)
226226+.Ss notify_get_state
227227+Get the 64-bit unsigned integer value associated with a token.
228228+The default value of a state variable is zero.
229229+(Available in Mac OS X 10.5 or later.)
230230+.Ss notify_suspend
231231+Suspends delivery of notifications for a notification token.
232232+Any notifications corresponding to a token that are posted while it is suspended
233233+will be coalesced, and pended until notifications are resumed using
234234+.Fn notify_resume .
235235+.Pp
236236+Calls to
237237+.Fn notify_suspend
238238+may be nested.
239239+Notifications will resume only when a matching number of calls are made to
240240+.Fn notify_resume .
241241+.Ss notify_resume
242242+Removes one level of suspension for a token previously suspended by a call to
243243+.Fn notify_suspend .
244244+When resumed, notifications will be delivered normally.
245245+A single notification will be generated if any notifications were pended while the token was suspended.
246246+.Ss notify_cancel
247247+Cancel notification and free resources associated with a notification
248248+token.
249249+Mach ports and file descriptor associated with a token are released
250250+(deallocated or closed) when all registration tokens associated with
251251+the port or file descriptor have been cancelled.
252252+.Pp
253253+.Ss notify_is_valid_token
254254+Determines if an integer value is valid for a current registration.
255255+Negative integers are never valid.
256256+A positive or zero value is valid if the current process has a registration associated with the given value.
257257+.Sh NAMESPACE CONVENTIONS
258258+Names in the namespace must be NULL-terminated.
259259+Names should be encoded as UTF-8 strings.
260260+.Pp
261261+The namespace supported by the system is unstructured, but users of
262262+this API are highly encouraged to follow the reverse-ICANN domain
263263+name convention used for Java package names and for System Preferences
264264+on Mac OS X.
265265+For example, "com.mydomain.example.event".
266266+.Pp
267267+Apple reserves the portion
268268+of the namespace prefixed by "com.apple.".
269269+This policy is not
270270+enforced in the current implementation, but may be in the future.
271271+.Pp
272272+Names in the space "user.uid.UID", where UID is a numeric user ID number
273273+are reserved for processes with that UID.
274274+Names in this protected space may only be accessed or modified by processes
275275+with the effective UID specified as the UID in the name.
276276+The name "user.uid.UID" is protected for the given UID, as are any
277277+names of the form "user.uid.UID.<sub-path>".
278278+In the latter case, the name must have a dot character following the UID.
279279+.Pp
280280+Third party developers are encouraged to choose a prefix for names
281281+that will avoid conflicts in the shared namespace.
282282+.Pp
283283+The portion of the namespace prefixed by the string "self." is set aside
284284+for private use by applications.
285285+That is, each client may use that part
286286+of the namespace for intra-process notifications.
287287+These notifications
288288+are private to each individual process and are not propagated between
289289+processes.
290290+.Sh USAGE EXAMPLES
291291+A notification producer.
292292+.Pp
293293+ #include <notify.h>
294294+ ...
295295+.Pp
296296+ notify_post("com.eg.random.event");
297297+.Pp
298298+A client using notify_check() to determine when to invalidate a cache.
299299+.Pp
300300+ #include <stdio.h>
301301+ #include <stdlib.h>
302302+ #include <notify.h>
303303+.Pp
304304+ int
305305+ main(int argc, char *argv[])
306306+ {
307307+ uint32_t status;
308308+ int token, check;
309309+.Pp
310310+ status = notify_register_check("com.eg.update", &token);
311311+ if (status != NOTIFY_STATUS_OK)
312312+ {
313313+ fprintf(stderr, "registration failed (%u)\\n", status);
314314+ exit(status);
315315+ }
316316+.Pp
317317+ build_my_cache();
318318+.Pp
319319+ ...
320320+.Pp
321321+ status = notify_check(token, &check);
322322+ if ((status == NOTIFY_STATUS_OK) && (check != 0))
323323+ {
324324+ /* An update has occurred - invalidate the cache */
325325+ reset_my_cache();
326326+ }
327327+.Pp
328328+ ...
329329+.Pp
330330+A client using file descriptor notifications.
331331+.Pp
332332+ #include <stdio.h>
333333+ #include <stdlib.h>
334334+ #include <string.h>
335335+ #include <errno.h>
336336+ #include <sys/types.h>
337337+ #include <sys/time.h>
338338+ #include <unistd.h>
339339+ #include <notify.h>
340340+.Pp
341341+ int
342342+ main(int argc, char *argv[])
343343+ {
344344+ uint32_t status;
345345+ int nf, rtoken, qtoken, t, ret;
346346+ fd_set readfds;
347347+.Pp
348348+ status = notify_register_file_descriptor("com.eg.random.event",
349349+ &nf, 0, &rtoken);
350350+ if (status != NOTIFY_STATUS_OK)
351351+ {
352352+ fprintf(stderr, "registration failed (%u)\\n", status);
353353+ exit(status);
354354+ }
355355+.Pp
356356+ status = notify_register_file_descriptor("com.eg.random.quit",
357357+ &nf, NOTIFY_REUSE, &qtoken);
358358+ if (status != NOTIFY_STATUS_OK)
359359+ {
360360+ fprintf(stderr, "registration failed (%u)\\n", status);
361361+ exit(status);
362362+ }
363363+.Pp
364364+ FD_ZERO(&readfds);
365365+ FD_SET(nf, &readfds);
366366+.Pp
367367+ for (;;)
368368+ {
369369+ ret = select(nf+1, &readfds, NULL, NULL, NULL);
370370+ if (ret <= 0) continue;
371371+ if (!FD_ISSET(nf, &readfds)) continue;
372372+.Pp
373373+ status = read(nf, &t, sizeof(int));
374374+ if (status < 0)
375375+ {
376376+ perror("read");
377377+ break;
378378+ }
379379+.Pp
380380+ t = ntohl(t);
381381+.Pp
382382+ if (t == rtoken) printf("random event\\n");
383383+ else if (t == qtoken) break;
384384+ }
385385+.Pp
386386+ printf("shutting down\\n");
387387+ notify_cancel(rtoken);
388388+ notify_cancel(qtoken);
389389+ exit(0);
390390+ }
391391+.Pp
392392+A client using dispatch notifications.
393393+.Pp
394394+ #include <stdio.h>
395395+ #include <stdlib.h>
396396+ #include <notify.h>
397397+ #include <dispatch/dispatch.h>
398398+.Pp
399399+ int
400400+ main(void)
401401+ {
402402+ uint32_t status;
403403+ int token;
404404+.Pp
405405+ status = notify_register_dispatch("com.eg.random.event", &token,
406406+ dispatch_get_main_queue(), ^(int t) {
407407+ printf("com.eg.random.event received!\\n"); });
408408+.Pp
409409+ dispatch_main();
410410+ exit(0);
411411+ }
412412+.Sh HISTORY
413413+These functions first appeared in
414414+Mac OS X 10.3.
415415+.Sh SEE ALSO
416416+.Xr ntohl 3 ,
417417+.Xr read 2 ,
418418+.Xr select 2 ,
419419+.Xr signal 3
+338
libnotify/notify.h
···11+/*
22+ * Copyright (c) 2003-2010 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * Portions Copyright (c) 2003-2010 Apple Inc. All Rights Reserved.
77+ *
88+ * This file contains Original Code and/or Modifications of Original Code
99+ * as defined in and that are subject to the Apple Public Source License
1010+ * Version 2.0 (the 'License'). You may not use this file except in
1111+ * compliance with the License. Please obtain a copy of the License at
1212+ * http://www.opensource.apple.com/apsl/ and read it before using this
1313+ * file.
1414+ *
1515+ * The Original Code and all software distributed under the License are
1616+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1717+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1818+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1919+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
2020+ * Please see the License for the specific language governing rights and
2121+ * limitations under the License.
2222+ *
2323+ * @APPLE_LICENSE_HEADER_END@
2424+ */
2525+2626+#ifndef __NOTIFICATION_H__
2727+#define __NOTIFICATION_H__
2828+2929+#include <sys/cdefs.h>
3030+#include <stdint.h>
3131+#include <mach/message.h>
3232+#include <Availability.h>
3333+#ifdef __BLOCKS__
3434+#include <dispatch/dispatch.h>
3535+#endif /* __BLOCKS__ */
3636+3737+/*! @header
3838+ * These routines allow processes to exchange stateless notification events.
3939+ * Processes post notifications to a single system-wide notification server,
4040+ * which then distributes notifications to client processes that have
4141+ * registered to receive those notifications, including processes run by
4242+ * other users.
4343+ *
4444+ * Notifications are associated with names in a namespace shared by all
4545+ * clients of the system. Clients may post notifications for names, and
4646+ * may monitor names for posted notifications. Clients may request
4747+ * notification delivery by a number of different methods.
4848+ *
4949+ * Clients desiring to monitor names in the notification system must
5050+ * register with the system, providing a name and other information
5151+ * required for the desired notification delivery method. Clients are
5252+ * given an integer token representing the registration.
5353+ *
5454+ * Note that the kernel provides limited queues for mach message and file
5555+ * descriptor messages. It is important to make sure that clients read
5656+ * mach ports and file descriptors frequently to prevent messages from
5757+ * being lost due to resource limitations. Clients that use signal-based
5858+ * notification should be aware that signals are not delivered to
5959+ * a process while it is running in a signal handler. This may affect
6060+ * the delivery of signals in close succession.
6161+ *
6262+ * Notifications may be coalesced in some cases. Multiple events posted
6363+ * for a name in rapid succession may result in a single notification sent
6464+ * to clients registered for notification for that name. Clients checking
6565+ * for changes using the notify_check() routine cannot determine if
6666+ * more than one event pas been posted since a previous call to
6767+ * notify_check() for that name.
6868+ *
6969+ * "False positives" may occur in notify_check() when used with a token
7070+ * generated by notify_register_check() due to implementation constraints.
7171+ * This behavior may vary in future releases.
7272+ *
7373+ * Synchronization between two processes may be achieved using the
7474+ * notify_set_state() and notify_get_state() routines.
7575+ */
7676+7777+/*! @defineblock Status Codes
7878+ * Status codes returned by the API.
7979+ */
8080+#define NOTIFY_STATUS_OK 0
8181+#define NOTIFY_STATUS_INVALID_NAME 1
8282+#define NOTIFY_STATUS_INVALID_TOKEN 2
8383+#define NOTIFY_STATUS_INVALID_PORT 3
8484+#define NOTIFY_STATUS_INVALID_FILE 4
8585+#define NOTIFY_STATUS_INVALID_SIGNAL 5
8686+#define NOTIFY_STATUS_INVALID_REQUEST 6
8787+#define NOTIFY_STATUS_NOT_AUTHORIZED 7
8888+#define NOTIFY_STATUS_FAILED 1000000
8989+/*! @/defineblock */
9090+9191+/*!
9292+ * Flag bits used for registration.
9393+ */
9494+#define NOTIFY_REUSE 0x00000001
9595+9696+9797+/*!
9898+ * Token values are zero or positive integers.
9999+ * NOTIFY_TOKEN_INVALID is useful as an initial value for
100100+ * a token value passed as an in/out parameter to one of
101101+ * the registration routines below.
102102+ */
103103+#define NOTIFY_TOKEN_INVALID -1
104104+105105+__BEGIN_DECLS
106106+107107+/*!
108108+ * Post a notification for a name.
109109+ *
110110+ * This is the only call that is required for a notification producer.
111111+ * Returns status.
112112+ */
113113+uint32_t notify_post(const char *name);
114114+115115+116116+#ifdef __BLOCKS__
117117+typedef void (^notify_handler_t)(int token);
118118+119119+/*!
120120+ * @function notify_register
121121+ * @abstract Request notification delivery to a dispatch queue.
122122+ * @discussion When notifications are received by the process, the notify
123123+ * subsystem will deliver the registered Block to the target
124124+ * dispatch queue. Notification blocks are not re-entrant,
125125+ * and subsequent notification Blocks will not be delivered
126126+ * for the same registration until the previous Block has
127127+ * returned.
128128+ * @param name (input) The notification name.
129129+ * @param out_token (output) The registration token.
130130+ * @param queue (input) The dispatch queue to which the Block is submitted.
131131+ * The dispatch queue is retained by the notify subsystem while
132132+ * the notification is registered, and will be released when
133133+ * notification is canceled.
134134+ * @param block (input) The Block to invoke on the dispatch queue in response
135135+ * to a notification. The notification token is passed to the
136136+ * Block as an argument so that the callee can modify the state
137137+ * of the notification or cancel the registration.
138138+ * @result Returns status.
139139+ */
140140+uint32_t notify_register_dispatch(const char *name, int *out_token, dispatch_queue_t queue, notify_handler_t handler)
141141+__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_3_2);
142142+#endif /* __BLOCKS__ */
143143+144144+/*!
145145+ * Creates a registration token be used with notify_check(),
146146+ * but no active notifications will be delivered.
147147+ *
148148+ * @param name
149149+ * (input) notification name
150150+ * @param out_token
151151+ * (output) registration token
152152+ * @result Returns status.
153153+ */
154154+uint32_t notify_register_check(const char *name, int *out_token);
155155+156156+/*!
157157+ * Request notification delivery by UNIX signal.
158158+ *
159159+ * A client may request signal notification for multiple names. After a signal
160160+ * is delivered, the notify_check() routine may be called with each notification
161161+ * token to determine which name (if any) generated the signal notification.
162162+ *
163163+ * @param name (input) notification name
164164+ * @param sig (input) signal number (see signal(3))
165165+ * @param out_token (output) notification token
166166+ * @result Returns status.
167167+ */
168168+uint32_t notify_register_signal(const char *name, int sig, int *out_token);
169169+170170+/*!
171171+ * Request notification by mach message.
172172+ *
173173+ * Notifications are delivered by an empty message sent to a mach port.
174174+ * By default, a new port is allocated and a pointer to it is returned
175175+ * as the value of "notify_port". A mach port previously returned by a
176176+ * call to this routine may be used for notifications if a pointer to that
177177+ * port is passed in to the routine and NOTIFY_REUSE is set in the flags
178178+ * parameter. The notification service must be able to extract send
179179+ * rights to the port.
180180+ *
181181+ * Note that the kernel limits the size of the message queue for any port.
182182+ * If it is important that notifications should not be lost due to queue
183183+ * overflow, clients should service messages quickly, and be careful about
184184+ * using the same port for notifications for more than one name.
185185+ *
186186+ * A notification message has an empty message body. The msgh_id field
187187+ * in the mach message header will have the value of the notification
188188+ * token. If a port is reused for multiple notification registrations,
189189+ * the msgh_id value may be used to determine which name generated
190190+ * the notification.
191191+ *
192192+ * @param name
193193+ * (input) notification name
194194+ * @param out_token
195195+ * (output) notification token
196196+ * @param notify_port
197197+ * (input/output) pointer to a mach port
198198+ * @result Returns status.
199199+ */
200200+uint32_t notify_register_mach_port(const char *name, mach_port_t *notify_port, int flags, int *out_token);
201201+202202+/*
203203+ * Request notification by a write to a file descriptor.
204204+ *
205205+ * Notifications are delivered by a write to a file descriptor.
206206+ * By default, a new file descriptor is created and a pointer to it
207207+ * is returned as the value of "notify_fd". A file descriptor created
208208+ * by a previous call to this routine may be used for notifications if
209209+ * a pointer to that file descriptor is passed in to the routine and
210210+ * NOTIFY_REUSE is set in the flags parameter.
211211+ *
212212+ * Note that the kernel limits the buffer space for queued writes on a
213213+ * file descriptor. If it is important that notifications should not be
214214+ * lost due to queue overflow, clients should service messages quickly,
215215+ * and be careful about using the same file descriptor for notifications
216216+ * for more than one name.
217217+ *
218218+ * Notifications are delivered by an integer value written to the
219219+ * file descriptor. The value will match the notification token
220220+ * for which the notification was generated.
221221+ *
222222+ * @param name
223223+ * (input) notification name
224224+ * @param out_token
225225+ * (output) notification token
226226+ * @param notify_fd
227227+ * (input/output) pointer to a file descriptor
228228+ * @result Returns status.
229229+ */
230230+uint32_t notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int *out_token);
231231+232232+/*!
233233+ * Check if any notifications have been posted.
234234+ *
235235+ * Output parameter check is set to 0 for false, 1 for true. Returns status.
236236+ * check is set to true the first time notify_check is called for a token.
237237+ * Subsequent calls set check to true when notifications have been posted for
238238+ * the name associated with the notification token. This routine is independent
239239+ * of notify_post(). That is, check will be true if an application calls
240240+ * notify_post() for a name and then calls notify_check() for a token associated
241241+ * with that name.
242242+ *
243243+ * @param token
244244+ * (input)notification token
245245+ * @param check
246246+ * (output) true/false indication
247247+ * @result Returns status.
248248+ */
249249+uint32_t notify_check(int token, int *check);
250250+251251+/*!
252252+ * Cancel notification and free resources associated with a notification
253253+ * token. Mach ports and file descriptor associated with a token are released
254254+ * (deallocated or closed) when all registration tokens associated with
255255+ * the port or file descriptor have been cancelled.
256256+ *
257257+ * @param token
258258+ * (input) notification token
259259+ * @result Returns status.
260260+ */
261261+uint32_t notify_cancel(int token);
262262+263263+/*!
264264+ * Suspend delivery of notifications for a token. Notifications for this token will be
265265+ * pended and coalesced, then delivered following a matching call to notify_resume.
266266+ * Calls to notify_suspend may be nested. Notifications remain suspended until
267267+ * an equal number of calls have been made to notify_resume.
268268+ *
269269+ * @param token
270270+ * (input) notification token
271271+ * @result Returns status.
272272+ */
273273+uint32_t notify_suspend(int token)
274274+__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0);
275275+276276+/*!
277277+ * Removes one level of suspension for a token previously suspended
278278+ * by a call to notify_suspend. Notifications will resume when a matching
279279+ * call to notify_resume is made for each previous call to notify_suspend.
280280+ * Notifications posted while a token is suspended are coalesced into
281281+ * a single notification sent following a resumption.
282282+ *
283283+ * @param token
284284+ * (input) notification token
285285+ * @result Returns status.
286286+ */
287287+uint32_t notify_resume(int token)
288288+__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0);
289289+290290+/*!
291291+ * Set or get a state value associated with a notification token.
292292+ * Each key in the notification namespace has an associated integer value available
293293+ * for use by clients as for application-specific purposes. A common usage is to
294294+ * allow two processes or threads to synchronize their activities. For example, a
295295+ * server process may need send a notification when a resource becomes available.
296296+ * A client process can register for the notification, but when it starts up it will
297297+ * not know whether the resource is available. The server can set the state value,
298298+ * and the client can check the value at startup time to synchronize with the server.
299299+ *
300300+ * Set the 64-bit integer state value.
301301+ *
302302+ * @param token
303303+ * (input) notification token
304304+ * @param state64
305305+ * (input) 64-bit unsigned integer value
306306+ * @result Returns status.
307307+ */
308308+uint32_t notify_set_state(int token, uint64_t state64)
309309+__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
310310+311311+/*!
312312+ * Get the 64-bit integer state value.
313313+ *
314314+ * @param token
315315+ * (input) notification token
316316+ * @param state64
317317+ * (output) 64-bit unsigned integer value
318318+ * @result Returns status.
319319+ */
320320+uint32_t notify_get_state(int token, uint64_t *state64)
321321+__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
322322+323323+/*!
324324+ * Determine if a token is valid (currently registered).
325325+ * Negative integer values are always invalid. Positive or
326326+ * zero values are valid only if they are associated with an
327327+ * existing registratiom.
328328+ *
329329+ * @param val
330330+ * (input) integer value
331331+ * @result Returns true if the value is a valid token, false otherwise.
332332+ */
333333+bool notify_is_valid_token(int val)
334334+__OSX_AVAILABLE_STARTING(__MAC_10_10,__IPHONE_8_0);
335335+336336+__END_DECLS
337337+338338+#endif /* __NOTIFICATION_H__ */
···11+/*
22+ * Copyright (c) 2012 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#include <dispatch/dispatch.h>
2525+#include <mach/mach.h>
2626+#include <pthread.h>
2727+#include <stdint.h>
2828+#include <TargetConditionals.h>
2929+3030+struct notify_globals_s {
3131+ // Global lock.
3232+ pthread_mutex_t notify_lock;
3333+3434+ int32_t notify_ipc_version;
3535+ pid_t notify_server_pid;
3636+3737+ uint32_t client_opts;
3838+ uint32_t saved_opts;
3939+4040+ // Last allocated name id.
4141+ uint64_t name_id;
4242+4343+ dispatch_once_t self_state_once;
4444+ notify_state_t *self_state;
4545+4646+ dispatch_once_t notify_server_port_once;
4747+ mach_port_t notify_server_port;
4848+ mach_port_t saved_server_port;
4949+5050+ mach_port_t notify_common_port;
5151+ int notify_common_token;
5252+ dispatch_source_t notify_dispatch_source;
5353+ dispatch_source_t server_proc_source;
5454+5555+ dispatch_once_t token_table_once;
5656+ table_t *token_table;
5757+ table_t *token_name_table;
5858+ uint32_t token_id;
5959+6060+ // File descriptor list.
6161+ uint32_t fd_count;
6262+ int *fd_clnt;
6363+ int *fd_srv;
6464+ int *fd_refcount;
6565+6666+ // Mach port list.
6767+ uint32_t mp_count;
6868+ mach_port_t *mp_list;
6969+ int *mp_refcount;
7070+ int *mp_mine;
7171+7272+ // Shared memory base address.
7373+ uint32_t *shm_base;
7474+};
7575+typedef struct notify_globals_s *notify_globals_t;
7676+7777+#if __has_include(<os/alloc_once_private.h>)
7878+#include <os/alloc_once_private.h>
7979+#if defined(OS_ALLOC_ONCE_KEY_LIBSYSTEM_NOTIFY)
8080+#define _NOTIFY_HAS_ALLOC_ONCE 1
8181+#endif
8282+#endif
8383+8484+__attribute__((visibility("hidden")))
8585+void _notify_init_globals(void * /* notify_globals_t */ globals);
8686+8787+__attribute__((visibility("hidden")))
8888+notify_globals_t _notify_globals_impl(void);
8989+9090+__attribute__((__pure__))
9191+static inline notify_globals_t
9292+_notify_globals(void) {
9393+#if _NOTIFY_HAS_ALLOC_ONCE
9494+ return (notify_globals_t)os_alloc_once(OS_ALLOC_ONCE_KEY_LIBSYSTEM_NOTIFY,
9595+ sizeof(struct notify_globals_s), &_notify_init_globals);
9696+#else
9797+ return _notify_globals_impl();
9898+#endif
9999+}
+373
libnotify/notify_ipc.defs
···11+/*
22+ * Copyright (c) 2003-2011 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#include <mach/std_types.defs>
2525+#include <mach/mach_types.defs>
2626+2727+subsystem notify_ipc 78945668;
2828+serverprefix _;
2929+3030+import <sys/types.h>;
3131+3232+import "notify_ipc_types.h";
3333+3434+type notify_name = ^ array [] of MACH_MSG_TYPE_BYTE
3535+ ctype : caddr_t;
3636+3737+routine _notify_server_post
3838+(
3939+ server : mach_port_t;
4040+ name : notify_name;
4141+ out status : int;
4242+ ServerAuditToken audit : audit_token_t
4343+);
4444+4545+routine _notify_server_register_plain
4646+(
4747+ server : mach_port_t;
4848+ name : notify_name;
4949+ out token : int;
5050+ out status : int;
5151+ ServerAuditToken audit : audit_token_t
5252+);
5353+5454+routine _notify_server_register_check
5555+(
5656+ server : mach_port_t;
5757+ name : notify_name;
5858+ out size : int;
5959+ out slot : int;
6060+ out token : int;
6161+ out status : int;
6262+ ServerAuditToken audit : audit_token_t
6363+);
6464+6565+routine _notify_server_register_signal
6666+(
6767+ server : mach_port_t;
6868+ name : notify_name;
6969+ sig: int;
7070+ out token : int;
7171+ out status : int;
7272+ ServerAuditToken audit : audit_token_t
7373+);
7474+7575+routine _notify_server_register_file_descriptor
7676+(
7777+ server : mach_port_t;
7878+ name : notify_name;
7979+ fileport : mach_port_move_send_t;
8080+ ntoken : int;
8181+ out token : int;
8282+ out status : int;
8383+ ServerAuditToken audit : audit_token_t
8484+);
8585+8686+routine _notify_server_register_mach_port
8787+(
8888+ server : mach_port_t;
8989+ name : notify_name;
9090+ port : mach_port_move_send_t;
9191+ ntoken : int;
9292+ out token : int;
9393+ out status : int;
9494+ ServerAuditToken audit : audit_token_t
9595+);
9696+9797+routine _notify_server_set_owner
9898+(
9999+ server : mach_port_t;
100100+ name : notify_name;
101101+ user : int;
102102+ group : int;
103103+ out status : int;
104104+ ServerAuditToken audit : audit_token_t
105105+);
106106+107107+routine _notify_server_get_owner
108108+(
109109+ server : mach_port_t;
110110+ name : notify_name;
111111+ out user : int;
112112+ out group : int;
113113+ out status : int;
114114+ ServerAuditToken audit : audit_token_t
115115+);
116116+117117+routine _notify_server_set_access
118118+(
119119+ server : mach_port_t;
120120+ name : notify_name;
121121+ mode : int;
122122+ out status : int;
123123+ ServerAuditToken audit : audit_token_t
124124+);
125125+126126+routine _notify_server_get_access
127127+(
128128+ server : mach_port_t;
129129+ name : notify_name;
130130+ out mode : int;
131131+ out status : int;
132132+ ServerAuditToken audit : audit_token_t
133133+);
134134+135135+routine _notify_server_release_name
136136+(
137137+ server : mach_port_t;
138138+ name : notify_name;
139139+ out status : int;
140140+ ServerAuditToken audit : audit_token_t
141141+);
142142+143143+routine _notify_server_cancel
144144+(
145145+ server : mach_port_t;
146146+ token : int;
147147+ out status : int;
148148+ ServerAuditToken audit : audit_token_t
149149+);
150150+151151+routine _notify_server_check
152152+(
153153+ server : mach_port_t;
154154+ token : int;
155155+ out check : int;
156156+ out status : int;
157157+ ServerAuditToken audit : audit_token_t
158158+);
159159+160160+routine _notify_server_get_state
161161+(
162162+ server : mach_port_t;
163163+ token : int;
164164+ out state : uint64_t;
165165+ out status : int;
166166+ ServerAuditToken audit : audit_token_t
167167+);
168168+169169+routine _notify_server_set_state
170170+(
171171+ server : mach_port_t;
172172+ token : int;
173173+ state : uint64_t;
174174+ out status : int;
175175+ ServerAuditToken audit : audit_token_t
176176+);
177177+178178+skip; /* formerly _notify_server_get_val */
179179+180180+skip; /* formerly _notify_server_set_val */
181181+182182+routine _notify_server_monitor_file
183183+(
184184+ server : mach_port_t;
185185+ token : int;
186186+ path : notify_name;
187187+ flags : int;
188188+ out status : int;
189189+ ServerAuditToken audit : audit_token_t
190190+);
191191+192192+routine _notify_server_suspend
193193+(
194194+ server : mach_port_t;
195195+ token : int;
196196+ out status : int;
197197+ ServerAuditToken audit : audit_token_t
198198+);
199199+200200+routine _notify_server_resume
201201+(
202202+ server : mach_port_t;
203203+ token : int;
204204+ out status : int;
205205+ ServerAuditToken audit : audit_token_t
206206+);
207207+208208+routine _notify_server_suspend_pid
209209+(
210210+ server : mach_port_t;
211211+ pid : int;
212212+ out status : int;
213213+ ServerAuditToken audit : audit_token_t
214214+);
215215+216216+routine _notify_server_resume_pid
217217+(
218218+ server : mach_port_t;
219219+ pid : int;
220220+ out status : int;
221221+ ServerAuditToken audit : audit_token_t
222222+);
223223+224224+simpleroutine _notify_server_simple_post
225225+(
226226+ server : mach_port_t;
227227+ name : notify_name;
228228+ ServerAuditToken audit : audit_token_t
229229+);
230230+231231+/* Additions for version 2 - more async support */
232232+233233+routine _notify_server_post_2
234234+(
235235+ server : mach_port_t;
236236+ name : notify_name;
237237+ out name_id : uint64_t;
238238+ out status : int;
239239+ ServerAuditToken audit : audit_token_t
240240+);
241241+242242+simpleroutine _notify_server_post_3
243243+(
244244+ server : mach_port_t;
245245+ name_id : uint64_t;
246246+ ServerAuditToken audit : audit_token_t
247247+);
248248+249249+simpleroutine _notify_server_post_4
250250+(
251251+ server : mach_port_t;
252252+ name : notify_name;
253253+ ServerAuditToken audit : audit_token_t
254254+);
255255+256256+simpleroutine _notify_server_register_plain_2
257257+(
258258+ server : mach_port_t;
259259+ name : notify_name;
260260+ token : int;
261261+ ServerAuditToken audit : audit_token_t
262262+);
263263+264264+routine _notify_server_register_check_2
265265+(
266266+ server : mach_port_t;
267267+ name : notify_name;
268268+ token: int;
269269+ out size : int;
270270+ out slot : int;
271271+ out name_id : uint64_t;
272272+ out status : int;
273273+ ServerAuditToken audit : audit_token_t
274274+);
275275+276276+simpleroutine _notify_server_register_signal_2
277277+(
278278+ server : mach_port_t;
279279+ name : notify_name;
280280+ token : int;
281281+ sig: int;
282282+ ServerAuditToken audit : audit_token_t
283283+);
284284+285285+simpleroutine _notify_server_register_file_descriptor_2
286286+(
287287+ server : mach_port_t;
288288+ name : notify_name;
289289+ token: int;
290290+ fileport : mach_port_move_send_t;
291291+ ServerAuditToken audit : audit_token_t
292292+);
293293+294294+simpleroutine _notify_server_register_mach_port_2
295295+(
296296+ server : mach_port_t;
297297+ name : notify_name;
298298+ token: int;
299299+ port : mach_port_move_send_t;
300300+ ServerAuditToken audit : audit_token_t
301301+);
302302+303303+simpleroutine _notify_server_cancel_2
304304+(
305305+ server : mach_port_t;
306306+ token : int;
307307+ ServerAuditToken audit : audit_token_t
308308+);
309309+310310+routine _notify_server_get_state_2
311311+(
312312+ server : mach_port_t;
313313+ name_id : uint64_t;
314314+ out state : uint64_t;
315315+ out status : int;
316316+ ServerAuditToken audit : audit_token_t
317317+);
318318+319319+routine _notify_server_get_state_3
320320+(
321321+ server : mach_port_t;
322322+ token : int;
323323+ out state : uint64_t;
324324+ out nid : uint64_t;
325325+ out status : int;
326326+ ServerAuditToken audit : audit_token_t
327327+);
328328+329329+simpleroutine _notify_server_set_state_2
330330+(
331331+ server : mach_port_t;
332332+ name_id : uint64_t;
333333+ state : uint64_t;
334334+ ServerAuditToken audit : audit_token_t
335335+);
336336+337337+routine _notify_server_set_state_3
338338+(
339339+ server : mach_port_t;
340340+ token : int;
341341+ state : uint64_t;
342342+ out nid : uint64_t;
343343+ out status : int;
344344+ ServerAuditToken audit : audit_token_t
345345+);
346346+347347+simpleroutine _notify_server_monitor_file_2
348348+(
349349+ server : mach_port_t;
350350+ token : int;
351351+ path : notify_name;
352352+ flags : int;
353353+ ServerAuditToken audit : audit_token_t
354354+);
355355+356356+routine _notify_server_regenerate
357357+(
358358+ server : mach_port_t;
359359+ name : notify_name;
360360+ token : int;
361361+ reg_type : uint32_t;
362362+ port : mach_port_make_send_t;
363363+ sig: int;
364364+ prev_slot: int;
365365+ prev_state : uint64_t;
366366+ prev_time : uint64_t;
367367+ path : notify_name;
368368+ path_flags: int;
369369+ out new_slot : int;
370370+ out new_name_id : uint64_t;
371371+ out status : int;
372372+ ServerAuditToken audit : audit_token_t
373373+);
+32
libnotify/notify_ipc_types.h
···11+/*
22+ * Copyright (c) 2003-2009 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * Portions Copyright (c) 2003-2009 Apple Inc. All Rights Reserved.
77+ *
88+ * This file contains Original Code and/or Modifications of Original Code
99+ * as defined in and that are subject to the Apple Public Source License
1010+ * Version 2.0 (the 'License'). You may not use this file except in
1111+ * compliance with the License. Please obtain a copy of the License at
1212+ * http://www.opensource.apple.com/apsl/ and read it before using this
1313+ * file.
1414+ *
1515+ * The Original Code and all software distributed under the License are
1616+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1717+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1818+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1919+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
2020+ * Please see the License for the specific language governing rights and
2121+ * limitations under the License.
2222+ *
2323+ * @APPLE_LICENSE_HEADER_END@
2424+ */
2525+2626+#ifndef _NOTIFY_IPC_TYPES_H_
2727+#define _NOTIFY_IPC_TYPES_H_
2828+2929+#include <sys/types.h>
3030+typedef char inline_data_t[2048];
3131+3232+#endif
···11+/*
22+ * Copyright (c) 2007-2009 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * Portions Copyright (c) 2007-2009 Apple Inc. All Rights Reserved.
77+ *
88+ * This file contains Original Code and/or Modifications of Original Code
99+ * as defined in and that are subject to the Apple Public Source License
1010+ * Version 2.0 (the 'License'). You may not use this file except in
1111+ * compliance with the License. Please obtain a copy of the License at
1212+ * http://www.opensource.apple.com/apsl/ and read it before using this
1313+ * file.
1414+ *
1515+ * The Original Code and all software distributed under the License are
1616+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1717+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1818+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1919+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
2020+ * Please see the License for the specific language governing rights and
2121+ * limitations under the License.
2222+ *
2323+ * @APPLE_LICENSE_HEADER_END@
2424+ */
2525+2626+/*
2727+ * This file lists notification keys that are posted using the
2828+ * notify_post() API by various Mac OS X system services.
2929+ * The exact circumstances under which services post these
3030+ * notifications is controlled by those services, and may change
3131+ * in future software releases.
3232+ */
3333+3434+/*
3535+ * Directory Service notifications
3636+ * These are posted by the DirectoryService daemon to advise clients that
3737+ * cached data should be invalidated.
3838+ */
3939+#define kNotifyDSCacheInvalidation "com.apple.system.DirectoryService.InvalidateCache"
4040+#define kNotifyDSCacheInvalidationGroup "com.apple.system.DirectoryService.InvalidateCache.group"
4141+#define kNotifyDSCacheInvalidationHost "com.apple.system.DirectoryService.InvalidateCache.host"
4242+#define kNotifyDSCacheInvalidationService "com.apple.system.DirectoryService.InvalidateCache.service"
4343+#define kNotifyDSCacheInvalidationUser "com.apple.system.DirectoryService.InvalidateCache.user"
4444+4545+/*
4646+ * File System notifications
4747+ * These advise clients of various filesystem events.
4848+ */
4949+#define kNotifyVFSMount "com.apple.system.kernel.mount"
5050+#define kNotifyVFSUnmount "com.apple.system.kernel.unmount"
5151+#define kNotifyVFSUpdate "com.apple.system.kernel.mountupdate"
5252+#define kNotifyVFSLowDiskSpace "com.apple.system.lowdiskspace"
5353+#define kNotifyVFSLowDiskSpaceRootFS "com.apple.system.lowdiskspace.system"
5454+#define kNotifyVFSLowDiskSpaceOtherFS "com.apple.system.lowdiskspace.user"
5555+5656+/*
5757+ * System Configuration notifications
5858+ * These advise clients of changes in the system configuration
5959+ * managed by the system configuration server (configd).
6060+ * Note that a much richer set of notifications are available to
6161+ * clients using the SCDynamicStore API.
6262+ */
6363+#define kNotifySCHostNameChange "com.apple.system.hostname"
6464+#define kNotifySCNetworkChange "com.apple.system.config.network_change"
6565+6666+/*
6767+ * ASL notifications
6868+ * Sent by syslogd to advise clients that new log messages have been
6969+ * added to the ASL database.
7070+ */
7171+#define kNotifyASLDBUpdate "com.apple.system.logger.message"
7272+7373+/*
7474+ * Time Zone change notification
7575+ * Sent by notifyd when the system's timezone changes.
7676+ */
7777+#define kNotifyTimeZoneChange "com.apple.system.timezone"
7878+7979+/*
8080+ * System clock change notification
8181+ * Sent when a process modifies the system clock using the settimeofday system call.
8282+ */
8383+#define kNotifyClockSet "com.apple.system.clock_set"
···11+/*
22+ * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#ifndef __NOTIFY_PRIVATE_H__
2525+#define __NOTIFY_PRIVATE_H__
2626+2727+#include <stdint.h>
2828+#include <Availability.h>
2929+3030+#define NOTIFY_OPT_DEMUX 0x00000001
3131+#define NOTIFY_OPT_REGEN 0x00000002
3232+#define NOTIFY_OPT_ENABLE 0x04000000
3333+#define NOTIFY_OPT_DISABLE 0x08000000
3434+3535+uint32_t notify_suspend_pid(pid_t pid)
3636+__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0);
3737+3838+uint32_t notify_resume_pid(pid_t pid)
3939+__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0);
4040+4141+uint32_t notify_simple_post(const char *name)
4242+__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3);
4343+4444+void notify_set_options(uint32_t opts)
4545+__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_6_0);
4646+4747+#endif /* __NOTIFY_PRIVATE_H__ */
···11+.\" Copyright (c) 2003-2013 Apple Inc. All rights reserved.
22+.\"
33+.\" @APPLE_LICENSE_HEADER_START@
44+.\"
55+.\" Portions Copyright (c) 2003-2010 Apple Inc. All Rights Reserved.
66+.\"
77+.\" This file contains Original Code and/or Modifications of Original Code
88+.\" as defined in and that are subject to the Apple Public Source License
99+.\" Version 2.0 (the 'License'). You may not use this file except in
1010+.\" compliance with the License. Please obtain a copy of the License at
1111+.\" http://www.opensource.apple.com/apsl/ and read it before using this
1212+.\" file.
1313+.\"
1414+.\" The Original Code and all software distributed under the License are
1515+.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1616+.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1717+.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1818+.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1919+.\" Please see the License for the specific language governing rights and
2020+.\" limitations under the License.
2121+.\"
2222+.\" @APPLE_LICENSE_HEADER_END@
2323+.\"
2424+.\"
2525+.Dd March 24, 2003
2626+.Dt notifyd 8
2727+.Os "Mac OS X"
2828+.Sh NAME
2929+.Nm notifyd
3030+.Nd notification server
3131+.Sh SYNOPSIS
3232+.Nm
3333+.Op Fl d
3434+.Op Fl log_file Ar path
3535+.Op Fl shm_pages Ar npages
3636+.Sh DESCRIPTION
3737+.Nm
3838+is the server for the Mac OS X notification system described in
3939+.Xr notify 3 .
4040+The server is started automatically by
4141+.Nm launchd
4242+during system startup.
4343+.Pp
4444+The
4545+.Fl d
4646+option causes
4747+.Nm notifyd
4848+to log debugging messages to a log file.
4949+Messages are not logged to ASL to avoid potential deadlocks,
5050+since the ASL system makes use of the
5151+.Xr notify 3
5252+system.
5353+.Pp
5454+The default log file is
5555+.Pa /var/log/notifyd.log .
5656+An alternate log file path may be specified following the
5757+.Fl log_file
5858+flag.
5959+.Pp
6060+The
6161+.Fl shm_pages Ar npages
6262+option sets the number of shared memory pages used for passive notification.
6363+The default is one page.
6464+If a value of zero is specified,
6565+shared memory is disabled and passive notifications are performed
6666+using IPC between the client and the server.
6767+.Sh SEE ALSO
6868+.Xr notify 3 .
+1265
libnotify/notifyd/notifyd.c
···11+/*
22+ * Copyright (c) 2003-2012 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#include <sys/types.h>
2525+#include <sys/stat.h>
2626+#include <signal.h>
2727+#include <stdio.h>
2828+#include <stdlib.h>
2929+#include <errno.h>
3030+#include <sys/un.h>
3131+#include <sys/ipc.h>
3232+#include <sys/mman.h>
3333+#include <sys/fcntl.h>
3434+#include <sys/syslimits.h>
3535+#include <sys/param.h>
3636+#include <sys/resource.h>
3737+#include <asl.h>
3838+#include <assert.h>
3939+#include <inttypes.h>
4040+#include <CrashReporterClient.h>
4141+#include <TargetConditionals.h>
4242+#include "pathwatch.h"
4343+#include "notifyd.h"
4444+#include "service.h"
4545+#include "pathwatch.h"
4646+#include "timer.h"
4747+4848+#include "notify_ipc.h"
4949+#include "notify_private.h"
5050+5151+#define forever for(;;)
5252+#define IndexNull -1
5353+5454+/* Compile flags */
5555+#define RUN_TIME_CHECKS
5656+5757+#if TARGET_IPHONE_SIMULATOR
5858+static const char *_config_file_path;
5959+#define CONFIG_FILE_PATH _config_file_path
6060+6161+static const char *_debug_log_path;
6262+#define DEBUG_LOG_PATH _debug_log_path
6363+#else
6464+#define CONFIG_FILE_PATH "/etc/notify.conf"
6565+#define DEBUG_LOG_PATH "/var/log/notifyd.log"
6666+#endif
6767+6868+#define STATUS_REQUEST_SHORT 0
6969+#define STATUS_REQUEST_LONG 1
7070+7171+#define N_NOTIFY_TYPES 6
7272+7373+static int notifyd_token;
7474+7575+static char *status_file = NULL;
7676+7777+typedef union
7878+{
7979+ mach_msg_header_t head;
8080+ union __RequestUnion__notify_ipc_subsystem request;
8181+} notify_request_msg;
8282+8383+typedef union
8484+{
8585+ mach_msg_header_t head;
8686+ union __ReplyUnion__notify_ipc_subsystem reply;
8787+} notify_reply_msg;
8888+8989+extern boolean_t notify_ipc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
9090+9191+static const char *
9292+notify_type_name(uint32_t t)
9393+{
9494+ switch (t)
9595+ {
9696+ case NOTIFY_TYPE_NONE: return "none ";
9797+ case NOTIFY_TYPE_MEMORY: return "memory";
9898+ case NOTIFY_TYPE_PLAIN: return "plain ";
9999+ case NOTIFY_TYPE_PORT: return "port ";
100100+ case NOTIFY_TYPE_FILE: return "file ";
101101+ case NOTIFY_TYPE_SIGNAL: return "signal";
102102+ default: return "unknown";
103103+ }
104104+105105+ return "unknown";
106106+}
107107+108108+static void
109109+fprint_client(FILE *f, client_t *c)
110110+{
111111+ int token;
112112+113113+ if (c == NULL)
114114+ {
115115+ fprintf(f, "NULL client\n");
116116+ return;
117117+ }
118118+119119+ token = c->client_id;
120120+121121+ fprintf(f, "client_id: %llu\n", c->client_id);
122122+ fprintf(f, "pid: %d\n", c->pid);
123123+ fprintf(f, "token: %d\n", token);
124124+ fprintf(f, "lastval: %u\n", c->lastval);
125125+ fprintf(f, "suspend_count: %u\n", c->suspend_count);
126126+ fprintf(f, "type: %s\n", notify_type_name(c->notify_type));
127127+ switch(c->notify_type)
128128+ {
129129+ case NOTIFY_TYPE_NONE:
130130+ break;
131131+132132+ case NOTIFY_TYPE_PLAIN:
133133+ break;
134134+135135+ case NOTIFY_TYPE_MEMORY:
136136+ break;
137137+138138+ case NOTIFY_TYPE_PORT:
139139+ fprintf(f, "mach port: 0x%08x\n", c->port);
140140+ break;
141141+142142+ case NOTIFY_TYPE_FILE:
143143+ fprintf(f, "fd: %d\n", c->fd);
144144+ break;
145145+146146+ case NOTIFY_TYPE_SIGNAL:
147147+ fprintf(f, "signal: %d\n", c->sig);
148148+ break;
149149+150150+ default: break;
151151+ }
152152+}
153153+154154+static void
155155+fprint_quick_client(FILE *f, client_t *c, int pname)
156156+{
157157+ int token;
158158+ if (c == NULL) return;
159159+160160+ token = c->client_id;
161161+162162+ if (pname == 1) fprintf(f, " [%s]", c->name_info->name);
163163+ fprintf(f, " %u %u", c->pid, token);
164164+ if (c->suspend_count > 0) fprintf(f, " suspend %d", c->suspend_count);
165165+ if (c->state & NOTIFY_CLIENT_STATE_PENDING) fprintf(f, " pending");
166166+ if (c->state & NOTIFY_CLIENT_STATE_TIMEOUT) fprintf(f, " timeout");
167167+ fprintf(f, " %s", notify_type_name(c->notify_type));
168168+ if (c->notify_type == NOTIFY_TYPE_SIGNAL) fprintf(f, " %d", c->sig);
169169+ fprintf(f, "\n");
170170+}
171171+172172+static void
173173+fprint_quick_name_info(FILE *f, name_info_t *n)
174174+{
175175+ list_t *sl;
176176+ client_t *c;
177177+178178+ if (n == NULL) return;
179179+180180+ fprintf(f, "\"%s\" uid=%u gid=%u %03x", n->name, n->uid, n->gid, n->access);
181181+ if (n->slot != -1)
182182+ {
183183+ fprintf(f, " slot %u", n->slot);
184184+ if (global.shared_memory_refcount[n->slot] != -1) fprintf(f, " = %u", global.shared_memory_base[n->slot]);
185185+ }
186186+187187+ fprintf(f, "\n");
188188+189189+ for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl))
190190+ {
191191+ c = _nc_list_data(sl);
192192+ if (c == NULL) break;
193193+194194+ fprint_quick_client(f, c, 0);
195195+ }
196196+197197+ fprintf(f, "\n");
198198+}
199199+200200+static void
201201+fprint_name_info(FILE *f, const char *name, name_info_t *n, table_t *pid_table, pid_t *max_pid)
202202+{
203203+ list_t *sl;
204204+ client_t *c;
205205+ uint32_t i, reg[N_NOTIFY_TYPES];
206206+207207+ if (n == NULL)
208208+ {
209209+ fprintf(f, "%s unknown\n", name);
210210+ return;
211211+ }
212212+213213+ fprintf(f, "name: %s\n", n->name);
214214+ fprintf(f, "id: %llu\n", n->name_id);
215215+ fprintf(f, "uid: %u\n", n->uid);
216216+ fprintf(f, "gid: %u\n", n->gid);
217217+ fprintf(f, "access: %03x\n", n->access);
218218+ fprintf(f, "refcount: %u\n", n->refcount);
219219+ if (n->slot == -1) fprintf(f, "slot: -unassigned-");
220220+ else
221221+ {
222222+ fprintf(f, "slot: %u", n->slot);
223223+ if (global.shared_memory_refcount[n->slot] != -1)
224224+ fprintf(f, " = %u (%u)", global.shared_memory_base[n->slot], global.shared_memory_refcount[n->slot]);
225225+ }
226226+ fprintf(f, "\n");
227227+ fprintf(f, "val: %u\n", n->val);
228228+ fprintf(f, "state: %llu\n", n->state);
229229+230230+ for (i = 0; i < N_NOTIFY_TYPES; i++) reg[i] = 0;
231231+232232+ for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl))
233233+ {
234234+ list_t *l;
235235+236236+ c = _nc_list_data(sl);
237237+ if (c == NULL) break;
238238+239239+ if ((c->pid != (pid_t)-1) && (c->pid > *max_pid)) *max_pid = c->pid;
240240+241241+ l = _nc_table_find_n(pid_table, c->pid);
242242+ if (l == NULL)
243243+ {
244244+ _nc_table_insert_n(pid_table, (uint32_t)c->pid, _nc_list_new(c));
245245+ }
246246+ else
247247+ {
248248+ _nc_list_concat(l, _nc_list_new(c));
249249+ }
250250+251251+ switch (c->notify_type)
252252+ {
253253+ case NOTIFY_TYPE_MEMORY: reg[1]++; break;
254254+ case NOTIFY_TYPE_PLAIN: reg[2]++; break;
255255+ case NOTIFY_TYPE_PORT: reg[3]++; break;
256256+ case NOTIFY_TYPE_FILE: reg[4]++; break;
257257+ case NOTIFY_TYPE_SIGNAL: reg[5]++; break;
258258+ default: reg[0]++;
259259+ }
260260+ }
261261+262262+ fprintf(f, "types: none %u memory %u plain %u port %u file %u signal %u\n", reg[0], reg[1], reg[2], reg[3], reg[4], reg[5]);
263263+264264+ for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl))
265265+ {
266266+ c = _nc_list_data(sl);
267267+ if (c == NULL) break;
268268+269269+ fprintf(f, "\n");
270270+ fprint_client(f, c);
271271+ }
272272+}
273273+274274+static void
275275+fprint_quick_status(FILE *f)
276276+{
277277+ void *tt;
278278+ name_info_t *n;
279279+280280+ tt = _nc_table_traverse_start(global.notify_state->name_table);
281281+282282+ while (tt != NULL)
283283+ {
284284+ n = _nc_table_traverse(global.notify_state->name_table, tt);
285285+ if (n == NULL) break;
286286+ fprint_quick_name_info(f, n);
287287+ }
288288+289289+ _nc_table_traverse_end(global.notify_state->name_table, tt);
290290+ fprintf(f, "\n");
291291+}
292292+293293+static void
294294+fprint_status(FILE *f)
295295+{
296296+ void *tt;
297297+ name_info_t *n;
298298+ int32_t i;
299299+ client_t *c;
300300+ svc_info_t *info;
301301+ path_node_t *node;
302302+ timer_t *timer;
303303+ table_t *pid_table;
304304+ pid_t pid, max_pid;
305305+ uint32_t count;
306306+ portproc_data_t *pdata;
307307+308308+ pid_table = _nc_table_new(0);
309309+ max_pid = 0;
310310+311311+ fprintf(f, "--- GLOBALS ---\n");
312312+ fprintf(f, "%u slots (current id %u)\n", global.nslots, global.slot_id);
313313+ fprintf(f, "%u log_cutoff (default %u)\n", global.log_cutoff, global.log_default);
314314+ fprintf(f, "\n");
315315+316316+ fprintf(f, "--- STATISTICS ---\n");
317317+ fprintf(f, "post %llu\n", call_statistics.post);
318318+ fprintf(f, " id %llu\n", call_statistics.post_by_id);
319319+ fprintf(f, " name %llu\n", call_statistics.post_by_name);
320320+ fprintf(f, " fetch %llu\n", call_statistics.post_by_name_and_fetch_id);
321321+ fprintf(f, " no_op %llu\n", call_statistics.post_no_op);
322322+ fprintf(f, "\n");
323323+ fprintf(f, "register %llu\n", call_statistics.reg);
324324+ fprintf(f, " plain %llu\n", call_statistics.reg_plain);
325325+ fprintf(f, " check %llu\n", call_statistics.reg_check);
326326+ fprintf(f, " signal %llu\n", call_statistics.reg_signal);
327327+ fprintf(f, " file %llu\n", call_statistics.reg_file);
328328+ fprintf(f, " port %llu\n", call_statistics.reg_port);
329329+ fprintf(f, "\n");
330330+ fprintf(f, "check %llu\n", call_statistics.check);
331331+ fprintf(f, "cancel %llu\n", call_statistics.cancel);
332332+ fprintf(f, "cleanup %llu\n", call_statistics.cleanup);
333333+ fprintf(f, "regenerate %llu\n", call_statistics.regenerate);
334334+ fprintf(f, "\n");
335335+ fprintf(f, "suspend %llu\n", call_statistics.suspend);
336336+ fprintf(f, "resume %llu\n", call_statistics.resume);
337337+ fprintf(f, "suspend_pid %llu\n", call_statistics.suspend_pid);
338338+ fprintf(f, "resume_pid %llu\n", call_statistics.resume_pid);
339339+ fprintf(f, "\n");
340340+ fprintf(f, "get_state %llu\n", call_statistics.get_state);
341341+ fprintf(f, " id %llu\n", call_statistics.get_state_by_id);
342342+ fprintf(f, " client %llu\n", call_statistics.get_state_by_client);
343343+ fprintf(f, " fetch %llu\n", call_statistics.get_state_by_client_and_fetch_id);
344344+ fprintf(f, "\n");
345345+ fprintf(f, "set_state %llu\n", call_statistics.set_state);
346346+ fprintf(f, " id %llu\n", call_statistics.set_state_by_id);
347347+ fprintf(f, " client %llu\n", call_statistics.set_state_by_client);
348348+ fprintf(f, " fetch %llu\n", call_statistics.set_state_by_client_and_fetch_id);
349349+ fprintf(f, "\n");
350350+ fprintf(f, "get_owner %llu\n", call_statistics.get_owner);
351351+ fprintf(f, "set_owner %llu\n", call_statistics.set_owner);
352352+ fprintf(f, "\n");
353353+ fprintf(f, "get_access %llu\n", call_statistics.get_access);
354354+ fprintf(f, "set_access %llu\n", call_statistics.set_access);
355355+ fprintf(f, "\n");
356356+ fprintf(f, "monitor %llu\n", call_statistics.monitor_file);
357357+ fprintf(f, "svc_path %llu\n", call_statistics.service_path);
358358+ fprintf(f, "svc_timer %llu\n", call_statistics.service_timer);
359359+360360+ fprintf(f, "\n");
361361+ fprintf(f, "name alloc %9u free %9u extant %9u\n", global.notify_state->stat_name_alloc , global.notify_state->stat_name_free, global.notify_state->stat_name_alloc - global.notify_state->stat_name_free);
362362+ fprintf(f, "subscription alloc %9u free %9u extant %9u\n", global.notify_state->stat_client_alloc , global.notify_state->stat_client_free, global.notify_state->stat_client_alloc - global.notify_state->stat_client_free);
363363+ fprintf(f, "portproc alloc %9u free %9u extant %9u\n", global.notify_state->stat_portproc_alloc , global.notify_state->stat_portproc_free, global.notify_state->stat_portproc_alloc - global.notify_state->stat_portproc_free);
364364+ fprintf(f, "\n");
365365+366366+ count = 0;
367367+ tt = _nc_table_traverse_start(global.notify_state->port_table);
368368+ while (tt != NULL)
369369+ {
370370+ pdata = _nc_table_traverse(global.notify_state->port_table, tt);
371371+ if (pdata == NULL) break;
372372+ count++;
373373+ }
374374+ _nc_table_traverse_end(global.notify_state->port_table, tt);
375375+ fprintf(f, "port count %u\n", count);
376376+377377+ count = 0;
378378+ tt = _nc_table_traverse_start(global.notify_state->proc_table);
379379+ while (tt != NULL)
380380+ {
381381+ pdata = _nc_table_traverse(global.notify_state->proc_table, tt);
382382+ if (pdata == NULL) break;
383383+ count++;
384384+ }
385385+ _nc_table_traverse_end(global.notify_state->proc_table, tt);
386386+ fprintf(f, "proc count %u\n", count);
387387+ fprintf(f, "\n");
388388+389389+ fprintf(f, "--- NAME TABLE ---\n");
390390+ count = 0;
391391+ tt = _nc_table_traverse_start(global.notify_state->name_table);
392392+393393+ while (tt != NULL)
394394+ {
395395+ n = _nc_table_traverse(global.notify_state->name_table, tt);
396396+ if (n == NULL) break;
397397+ fprint_name_info(f, n->name, n, pid_table, &max_pid);
398398+ fprintf(f, "\n");
399399+ count++;
400400+ }
401401+402402+ fprintf(f, "--- NAME COUNT %u ---\n", count);
403403+ _nc_table_traverse_end(global.notify_state->name_table, tt);
404404+ fprintf(f, "\n");
405405+406406+ fprintf(f, "--- SUBSCRIPTION TABLE ---\n");
407407+ count = 0;
408408+ tt = _nc_table_traverse_start(global.notify_state->client_table);
409409+410410+ while (tt != NULL)
411411+ {
412412+ c = _nc_table_traverse(global.notify_state->client_table, tt);
413413+ if (c == NULL) break;
414414+ fprint_quick_client(f, c, 1);
415415+ count++;
416416+ }
417417+418418+ fprintf(f, "--- SUBSCRIPTION COUNT %u ---\n", count);
419419+ _nc_table_traverse_end(global.notify_state->client_table, tt);
420420+ fprintf(f, "\n");
421421+422422+ fprintf(f, "--- CONTROLLED NAME ---\n");
423423+ for (i = 0; i < global.notify_state->controlled_name_count; i++)
424424+ {
425425+ fprintf(f, "%s %u %u %03x\n", global.notify_state->controlled_name[i]->name, global.notify_state->controlled_name[i]->uid, global.notify_state->controlled_name[i]->gid, global.notify_state->controlled_name[i]->access);
426426+ }
427427+ fprintf(f, "--- CONTROLLED NAME COUNT %u ---\n", global.notify_state->controlled_name_count);
428428+ fprintf(f, "\n");
429429+430430+ fprintf(f, "--- PUBLIC SERVICE ---\n");
431431+ count = 0;
432432+ tt = _nc_table_traverse_start(global.notify_state->name_table);
433433+ while (tt != NULL)
434434+ {
435435+ n = _nc_table_traverse(global.notify_state->name_table, tt);
436436+ if (n == NULL) break;
437437+ if (n->private == NULL) continue;
438438+439439+ count++;
440440+ info = (svc_info_t *)n->private;
441441+442442+ if (info->type == 0)
443443+ {
444444+ fprintf(f, "Null service: %s\n", n->name);
445445+ }
446446+ if (info->type == SERVICE_TYPE_PATH_PUBLIC)
447447+ {
448448+ node = (path_node_t *)info->private;
449449+ fprintf(f, "Path Service: %s <- %s\n", n->name, node->path);
450450+ }
451451+ else if (info->type == SERVICE_TYPE_TIMER_PUBLIC)
452452+ {
453453+ timer = (timer_t *)info->private;
454454+ switch (timer->type)
455455+ {
456456+ case TIME_EVENT_ONESHOT:
457457+ {
458458+ fprintf(f, "Time Service: %s <- Oneshot %llu\n", n->name, timer->start);
459459+ break;
460460+ }
461461+ case TIME_EVENT_CLOCK:
462462+ {
463463+ fprintf(f, "Time Service: %s <- Clock start %lld freq %u end %lld\n", n->name, timer->start, timer->freq, timer->end);
464464+ break;
465465+ }
466466+ case TIME_EVENT_CAL:
467467+ {
468468+ fprintf(f, "Time Service: %s <- Calendar start %lld freq %u end %lld day %d\n", n->name, timer->start, timer->freq, timer->end, timer->day);
469469+ break;
470470+ }
471471+ }
472472+ }
473473+ else
474474+ {
475475+ fprintf(f, "Unknown service: %s (%u)\n", n->name, info->type);
476476+ }
477477+ }
478478+479479+ fprintf(f, "--- PUBLIC SERVICE COUNT %u ---\n", count);
480480+ _nc_table_traverse_end(global.notify_state->name_table, tt);
481481+ fprintf(f, "\n");
482482+483483+ fprintf(f, "--- PRIVATE SERVICE ---\n");
484484+ count = 0;
485485+ tt = _nc_table_traverse_start(global.notify_state->client_table);
486486+ while (tt != NULL)
487487+ {
488488+ c = _nc_table_traverse(global.notify_state->client_table, tt);
489489+ if (c == NULL) break;
490490+ if (c->private == NULL) continue;
491491+492492+ count++;
493493+ info = (svc_info_t *)c->private;
494494+ n = c->name_info;
495495+496496+ if (info->type == 0)
497497+ {
498498+ fprintf(f, "PID %u Null service: %s\n", c->pid, n->name);
499499+ }
500500+ if (info->type == SERVICE_TYPE_PATH_PRIVATE)
501501+ {
502502+ node = (path_node_t *)info->private;
503503+ fprintf(f, "PID %u Path Service: %s <- %s (UID %d GID %d)\n", c->pid, n->name, node->path, node->uid, node->gid);
504504+ }
505505+ else if (info->type == SERVICE_TYPE_TIMER_PRIVATE)
506506+ {
507507+ timer = (timer_t *)info->private;
508508+ switch (timer->type)
509509+ {
510510+ case TIME_EVENT_ONESHOT:
511511+ {
512512+ fprintf(f, "PID %u Time Service: %s <- Oneshot %"PRId64"\n", c->pid, n->name, timer->start);
513513+ break;
514514+ }
515515+ case TIME_EVENT_CLOCK:
516516+ {
517517+ fprintf(f, "PID %u Time Service: %s <- Clock start %"PRId64" freq %"PRIu32" end %"PRId64"\n", c->pid, n->name, timer->start, timer->freq, timer->end);
518518+ break;
519519+ }
520520+ case TIME_EVENT_CAL:
521521+ {
522522+ fprintf(f, "PID %u Time Service: %s <- Calendar start %"PRId64" freq %"PRIu32" end %"PRId64" day %"PRId32"\n", c->pid, n->name, timer->start, timer->freq, timer->end, timer->day);
523523+ break;
524524+ }
525525+ }
526526+ }
527527+ }
528528+529529+ fprintf(f, "--- PRIVATE SERVICE COUNT %u ---\n", count);
530530+ _nc_table_traverse_end(global.notify_state->client_table, tt);
531531+ fprintf(f, "\n");
532532+533533+ fprintf(f, "--- PROCESSES ---\n");
534534+ for (pid = 0; pid <= max_pid; pid++)
535535+ {
536536+ int mem_count, plain_count, file_count, port_count, sig_count, com_port_count;
537537+ mach_port_t common_port = MACH_PORT_NULL;
538538+539539+ list_t *x;
540540+ list_t *l = _nc_table_find_n(pid_table, pid);
541541+ if (l == NULL) continue;
542542+543543+ mem_count = 0;
544544+ plain_count = 0;
545545+ file_count = 0;
546546+ port_count = 0;
547547+ sig_count = 0;
548548+ com_port_count = 0;
549549+550550+ for (x = l; x != NULL; x = _nc_list_next(x))
551551+ {
552552+ c = _nc_list_data(x);
553553+ if (c != NULL)
554554+ {
555555+ if ((c->notify_type == NOTIFY_TYPE_PORT) && (!strcmp(c->name_info->name, COMMON_PORT_KEY))) common_port = c->port;
556556+ }
557557+ }
558558+559559+ for (x = l; x != NULL; x = _nc_list_next(x))
560560+ {
561561+ c = _nc_list_data(x);
562562+ if (c != NULL)
563563+ {
564564+ switch(c->notify_type)
565565+ {
566566+ case NOTIFY_TYPE_NONE:
567567+ break;
568568+569569+ case NOTIFY_TYPE_PLAIN:
570570+ plain_count++;
571571+ break;
572572+573573+ case NOTIFY_TYPE_MEMORY:
574574+ mem_count++;
575575+ break;
576576+577577+ case NOTIFY_TYPE_PORT:
578578+ port_count++;
579579+ if (c->port == common_port) com_port_count++;
580580+ break;
581581+582582+ case NOTIFY_TYPE_FILE:
583583+ file_count++;
584584+ break;
585585+586586+ case NOTIFY_TYPE_SIGNAL:
587587+ sig_count++;
588588+ break;
589589+590590+ default: break;
591591+ }
592592+ }
593593+ }
594594+595595+ fprintf(f, "pid: %u ", pid);
596596+ if (file_count + sig_count == 0)
597597+ {
598598+ if (port_count == 0) fprintf(f, "regenerable / polling\n");
599599+ else if (port_count == com_port_count) fprintf(f, "regenerable\n");
600600+ else if (com_port_count > 0) fprintf(f, "partially regenerable\n");
601601+ else fprintf(f, "non-regenerable\n");
602602+ }
603603+ else
604604+ {
605605+ if (com_port_count == 0) fprintf(f, "non-regenerable\n");
606606+ else fprintf(f, "partially regenerable\n");
607607+ }
608608+609609+ fprintf(f, "memory %u plain %u port %u file %u signal %u common port %u\n", mem_count, plain_count, port_count, file_count, sig_count, com_port_count);
610610+ for (x = l; x != NULL; x = _nc_list_next(x))
611611+ {
612612+ c = _nc_list_data(x);
613613+ if (c != NULL)
614614+ {
615615+ fprintf(f, " %s: %s\n", notify_type_name(c->notify_type), c->name_info->name);
616616+ }
617617+ }
618618+619619+ fprintf(f, "\n");
620620+ _nc_list_release_list(l);
621621+ }
622622+ fprintf(f, "\n");
623623+ _nc_table_free(pid_table);
624624+}
625625+626626+void
627627+dump_status(uint32_t level)
628628+{
629629+ FILE *f;
630630+631631+ if (status_file == NULL)
632632+ {
633633+ asprintf(&status_file, "/var/run/notifyd_%u.status", getpid());
634634+ if (status_file == NULL) return;
635635+ }
636636+637637+ unlink(status_file);
638638+ f = fopen(status_file, "w");
639639+ if (f == NULL) return;
640640+641641+ if (level == STATUS_REQUEST_SHORT) fprint_quick_status(f);
642642+ else if (level == STATUS_REQUEST_LONG) fprint_status(f);
643643+644644+ fclose(f);
645645+}
646646+647647+void
648648+log_message(int priority, const char *str, ...)
649649+{
650650+ time_t t;
651651+ char now[32];
652652+ va_list ap;
653653+ FILE *lfp;
654654+655655+ if (priority > global.log_cutoff) return;
656656+ if (global.log_path == NULL) return;
657657+658658+ lfp = fopen(global.log_path, "a");
659659+ if (lfp == NULL) return;
660660+661661+ va_start(ap, str);
662662+663663+ t = time(NULL);
664664+ memset(now, 0, 32);
665665+ strftime(now, 32, "%b %e %T", localtime(&t));
666666+667667+ fprintf(lfp, "%s: ", now);
668668+ vfprintf(lfp, str, ap);
669669+ fflush(lfp);
670670+671671+ va_end(ap);
672672+ fclose(lfp);
673673+}
674674+675675+uint32_t
676676+daemon_post(const char *name, uint32_t u, uint32_t g)
677677+{
678678+ name_info_t *n;
679679+ uint32_t status;
680680+681681+ if (name == NULL) return 0;
682682+683683+ n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name);
684684+ if (n == NULL) return 0;
685685+686686+ if (n->slot != (uint32_t)-1) global.shared_memory_base[n->slot]++;
687687+688688+ status = _notify_lib_post(global.notify_state, name, u, g);
689689+ return status;
690690+}
691691+692692+uint32_t
693693+daemon_post_nid(uint64_t nid, uint32_t u, uint32_t g)
694694+{
695695+ name_info_t *n;
696696+ uint32_t status;
697697+698698+ n = (name_info_t *)_nc_table_find_64(global.notify_state->name_id_table, nid);
699699+ if (n == NULL) return 0;
700700+701701+ if (n->slot != (uint32_t)-1) global.shared_memory_base[n->slot]++;
702702+703703+ status = _notify_lib_post_nid(global.notify_state, nid, u, g);
704704+ return status;
705705+}
706706+707707+void
708708+daemon_post_client(uint64_t cid)
709709+{
710710+ client_t *c;
711711+712712+ c = _nc_table_find_64(global.notify_state->client_table, cid);
713713+ if (c == NULL) return;
714714+715715+ if ((c->notify_type == NOTIFY_TYPE_MEMORY) && (c->name_info != NULL) && (c->name_info->slot != (uint32_t)-1))
716716+ {
717717+ global.shared_memory_base[c->name_info->slot]++;
718718+ }
719719+720720+ _notify_lib_post_client(global.notify_state, c);
721721+}
722722+723723+void
724724+daemon_set_state(const char *name, uint64_t val)
725725+{
726726+ name_info_t *n;
727727+728728+ if (name == NULL) return;
729729+730730+ n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name);
731731+ if (n == NULL) return;
732732+733733+ n->state = val;
734734+}
735735+736736+static void
737737+init_launch_config(const char *name)
738738+{
739739+ launch_data_t tmp, pdict;
740740+741741+ tmp = launch_data_new_string(LAUNCH_KEY_CHECKIN);
742742+ global.launch_dict = launch_msg(tmp);
743743+ launch_data_free(tmp);
744744+745745+ if (global.launch_dict == NULL)
746746+ {
747747+ fprintf(stderr, "%d launchd checkin failed\n", getpid());
748748+ exit(1);
749749+ }
750750+751751+ tmp = launch_data_dict_lookup(global.launch_dict, LAUNCH_JOBKEY_MACHSERVICES);
752752+ if (tmp == NULL)
753753+ {
754754+ fprintf(stderr, "%d launchd lookup of LAUNCH_JOBKEY_MACHSERVICES failed\n", getpid());
755755+ exit(1);
756756+ }
757757+758758+ pdict = launch_data_dict_lookup(tmp, name);
759759+ if (pdict == NULL)
760760+ {
761761+ fprintf(stderr, "%d launchd lookup of name %s failed\n", getpid(), name);
762762+ exit(1);
763763+ }
764764+765765+ global.server_port = launch_data_get_machport(pdict);
766766+ if (global.server_port == MACH_PORT_NULL)
767767+ {
768768+ fprintf(stderr, "%d launchd lookup of server port for name %s failed\n", getpid(), name);
769769+ exit(1);
770770+ }
771771+}
772772+773773+static void
774774+string_list_free(char **l)
775775+{
776776+ int i;
777777+778778+ if (l == NULL) return;
779779+ for (i = 0; l[i] != NULL; i++)
780780+ {
781781+ if (l[i] != NULL) free(l[i]);
782782+ l[i] = NULL;
783783+ }
784784+ free(l);
785785+}
786786+787787+static char **
788788+string_insert(char *s, char **l, unsigned int x)
789789+{
790790+ int i, len;
791791+792792+ if (s == NULL) return l;
793793+ if (l == NULL)
794794+ {
795795+ l = (char **)malloc(2 * sizeof(char *));
796796+ l[0] = strdup(s);
797797+ l[1] = NULL;
798798+ return l;
799799+ }
800800+801801+ for (i = 0; l[i] != NULL; i++);
802802+ len = i + 1; /* count the NULL on the end of the list too! */
803803+804804+ l = (char **)realloc(l, (len + 1) * sizeof(char *));
805805+806806+ if ((x >= (len - 1)) || (x == IndexNull))
807807+ {
808808+ l[len - 1] = strdup(s);
809809+ l[len] = NULL;
810810+ return l;
811811+ }
812812+813813+ for (i = len; i > x; i--) l[i] = l[i - 1];
814814+ l[x] = strdup(s);
815815+ return l;
816816+}
817817+818818+static char **
819819+string_append(char *s, char **l)
820820+{
821821+ return string_insert(s, l, IndexNull);
822822+}
823823+824824+static unsigned int
825825+string_list_length(char **l)
826826+{
827827+ int i;
828828+829829+ if (l == NULL) return 0;
830830+ for (i = 0; l[i] != NULL; i++);
831831+ return i;
832832+}
833833+834834+static unsigned int
835835+string_index(char c, char *s)
836836+{
837837+ int i;
838838+ char *p;
839839+840840+ if (s == NULL) return IndexNull;
841841+842842+ for (i = 0, p = s; p[0] != '\0'; p++, i++)
843843+ {
844844+ if (p[0] == c) return i;
845845+ }
846846+847847+ return IndexNull;
848848+}
849849+850850+static char **
851851+explode(char *s, char *delim)
852852+{
853853+ char **l = NULL;
854854+ char *p, *t;
855855+ int i, n;
856856+857857+ if (s == NULL) return NULL;
858858+859859+ p = s;
860860+ while (p[0] != '\0')
861861+ {
862862+ for (i = 0; ((p[i] != '\0') && (string_index(p[i], delim) == IndexNull)); i++);
863863+ n = i;
864864+ t = malloc(n + 1);
865865+ for (i = 0; i < n; i++) t[i] = p[i];
866866+ t[n] = '\0';
867867+ l = string_append(t, l);
868868+ free(t);
869869+ t = NULL;
870870+ if (p[i] == '\0') return l;
871871+ if (p[i + 1] == '\0') l = string_append("", l);
872872+ p = p + i + 1;
873873+ }
874874+ return l;
875875+}
876876+877877+static uint32_t
878878+atoaccess(char *s)
879879+{
880880+ uint32_t a;
881881+882882+ if (s == NULL) return 0;
883883+ if (strlen(s) != 6) return 0;
884884+885885+ a = 0;
886886+ if (s[0] == 'r') a |= (NOTIFY_ACCESS_READ << NOTIFY_ACCESS_USER_SHIFT);
887887+ if (s[1] == 'w') a |= (NOTIFY_ACCESS_WRITE << NOTIFY_ACCESS_USER_SHIFT);
888888+889889+ if (s[2] == 'r') a |= (NOTIFY_ACCESS_READ << NOTIFY_ACCESS_GROUP_SHIFT);
890890+ if (s[3] == 'w') a |= (NOTIFY_ACCESS_WRITE << NOTIFY_ACCESS_GROUP_SHIFT);
891891+892892+ if (s[4] == 'r') a |= (NOTIFY_ACCESS_READ << NOTIFY_ACCESS_OTHER_SHIFT);
893893+ if (s[5] == 'w') a |= (NOTIFY_ACCESS_WRITE << NOTIFY_ACCESS_OTHER_SHIFT);
894894+895895+ return a;
896896+}
897897+898898+static void
899899+init_config()
900900+{
901901+ FILE *f;
902902+ struct stat sb;
903903+ char line[1024];
904904+ char **args;
905905+ uint32_t status, argslen;
906906+ uint32_t uid, gid, access;
907907+ uint64_t nid, val64;
908908+909909+ /*
910910+ * Set IPC Version Number & PID
911911+ */
912912+ val64 = getpid();
913913+ val64 <<= 32;
914914+ val64 |= NOTIFY_IPC_VERSION;
915915+916916+ _notify_lib_register_plain(global.notify_state, NOTIFY_IPC_VERSION_NAME, -1, notifyd_token++, -1, 0, 0, &nid);
917917+ _notify_lib_set_state(global.notify_state, nid, val64, 0, 0);
918918+919919+ /* Check config file */
920920+ if (stat(CONFIG_FILE_PATH, &sb) != 0) return;
921921+922922+ if (sb.st_uid != 0)
923923+ {
924924+ log_message(ASL_LEVEL_ERR, "config file %s not owned by root: ignored\n", CONFIG_FILE_PATH);
925925+ return;
926926+ }
927927+928928+ if (sb.st_mode & 02)
929929+ {
930930+ log_message(ASL_LEVEL_ERR, "config file %s is world-writable: ignored\n", CONFIG_FILE_PATH);
931931+ return;
932932+ }
933933+934934+ /* Read config file */
935935+ f = fopen(CONFIG_FILE_PATH, "r");
936936+ if (f == NULL) return;
937937+938938+ forever
939939+ {
940940+ if (fgets(line, 1024, f) == NULL) break;
941941+ if (line[0] == '\0') continue;
942942+ if (line[0] == '#') continue;
943943+944944+ line[strlen(line) - 1] = '\0';
945945+ args = explode(line, "\t ");
946946+ argslen = string_list_length(args);
947947+ if (argslen == 0) continue;
948948+949949+ if (!strcasecmp(args[0], "monitor"))
950950+ {
951951+ if (argslen < 3)
952952+ {
953953+ string_list_free(args);
954954+ continue;
955955+ }
956956+ _notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid);
957957+ service_open_path(args[1], args[2], 0, 0);
958958+ }
959959+960960+ if (!strcasecmp(args[0], "timer"))
961961+ {
962962+ if (argslen < 3)
963963+ {
964964+ string_list_free(args);
965965+ continue;
966966+ }
967967+ _notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid);
968968+ status = service_open_timer(args[1], args[2]);
969969+ }
970970+971971+ else if (!strcasecmp(args[0], "set"))
972972+ {
973973+ if (argslen < 3)
974974+ {
975975+ string_list_free(args);
976976+ continue;
977977+ }
978978+979979+ val64 = atoll(args[2]);
980980+981981+ _notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid);
982982+ _notify_lib_set_state(global.notify_state, nid, val64, 0, 0);
983983+ }
984984+985985+ else if (!strcasecmp(args[0], "reserve"))
986986+ {
987987+ if (argslen == 1)
988988+ {
989989+ string_list_free(args);
990990+ continue;
991991+ }
992992+993993+ uid = 0;
994994+ gid = 0;
995995+ access = NOTIFY_ACCESS_DEFAULT;
996996+997997+ if (argslen > 2) uid = atoi(args[2]);
998998+ if (argslen > 3) gid = atoi(args[3]);
999999+ if (argslen > 4) access = atoaccess(args[4]);
10001000+10011001+ if ((uid != 0) || (gid != 0)) _notify_lib_set_owner(global.notify_state, args[1], uid, gid);
10021002+ if (access != NOTIFY_ACCESS_DEFAULT) _notify_lib_set_access(global.notify_state, args[1], access);
10031003+ }
10041004+ else if (!strcasecmp(args[0], "quit"))
10051005+ {
10061006+ string_list_free(args);
10071007+ break;
10081008+ }
10091009+10101010+ string_list_free(args);
10111011+ }
10121012+10131013+ fclose(f);
10141014+}
10151015+10161016+static void
10171017+service_mach_message(bool blocking)
10181018+{
10191019+ __block kern_return_t status;
10201020+ uint32_t rbits, sbits;
10211021+ notify_request_msg *request;
10221022+ notify_reply_msg *reply;
10231023+ char rbuf[sizeof(notify_request_msg) + MAX_TRAILER_SIZE];
10241024+ char sbuf[sizeof(notify_reply_msg) + MAX_TRAILER_SIZE];
10251025+10261026+ forever
10271027+ {
10281028+ memset(rbuf, 0, sizeof(rbuf));
10291029+ memset(sbuf, 0, sizeof(sbuf));
10301030+10311031+ request = (notify_request_msg *)rbuf;
10321032+ reply = (notify_reply_msg *)sbuf;
10331033+10341034+ request->head.msgh_local_port = global.server_port;
10351035+ request->head.msgh_size = global.request_size;
10361036+10371037+ rbits = MACH_RCV_MSG | (blocking ? 0 : MACH_RCV_TIMEOUT) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | MACH_RCV_VOUCHER;
10381038+ sbits = MACH_SEND_MSG;
10391039+10401040+ status = mach_msg(&(request->head), rbits, 0, global.request_size, global.server_port, 0, MACH_PORT_NULL);
10411041+ if (status != KERN_SUCCESS) return;
10421042+10431043+ voucher_mach_msg_state_t voucher = voucher_mach_msg_adopt(&(request->head));
10441044+10451045+#if TARGET_OS_EMBEDDED
10461046+ /* Synchronize with work_q since on embedded main() calls this
10471047+ * from the global concurrent queue. */
10481048+ dispatch_sync(global.work_q, ^{
10491049+ status = notify_ipc_server(&(request->head), &(reply->head));
10501050+ });
10511051+#else
10521052+ status = notify_ipc_server(&(request->head), &(reply->head));
10531053+#endif
10541054+10551055+ if (!status && (request->head.msgh_bits & MACH_MSGH_BITS_COMPLEX))
10561056+ {
10571057+ /* destroy the request - but not the reply port */
10581058+ request->head.msgh_remote_port = MACH_PORT_NULL;
10591059+ mach_msg_destroy(&(request->head));
10601060+ }
10611061+10621062+ if (reply->head.msgh_remote_port)
10631063+ {
10641064+ status = mach_msg(&(reply->head), sbits, reply->head.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
10651065+ if (status == MACH_SEND_INVALID_DEST || status == MACH_SEND_TIMED_OUT)
10661066+ {
10671067+ /* deallocate reply port rights not consumed by failed mach_msg() send */
10681068+ mach_msg_destroy(&(reply->head));
10691069+ }
10701070+ }
10711071+10721072+ voucher_mach_msg_revert(voucher);
10731073+ }
10741074+}
10751075+10761076+static int32_t
10771077+open_shared_memory(const char *name)
10781078+{
10791079+ int32_t shmfd, isnew;
10801080+ uint32_t size;
10811081+10821082+ size = global.nslots * sizeof(uint32_t);
10831083+10841084+ isnew = 1;
10851085+ shmfd = shm_open(name, O_RDWR, 0644);
10861086+ if (shmfd != -1)
10871087+ {
10881088+ isnew = 0;
10891089+ }
10901090+ else
10911091+ {
10921092+ shmfd = shm_open(name, O_RDWR | O_CREAT, 0644);
10931093+ }
10941094+10951095+ if (shmfd == -1)
10961096+ {
10971097+ char error_message[1024];
10981098+ snprintf(error_message, sizeof(error_message), "shm_open %s failed: %s\n", name, strerror(errno));
10991099+11001100+ CRSetCrashLogMessage(error_message);
11011101+ log_message(ASL_LEVEL_NOTICE, "%s", error_message);
11021102+ return -1;
11031103+ }
11041104+11051105+ ftruncate(shmfd, size);
11061106+ global.shared_memory_base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
11071107+ close(shmfd);
11081108+11091109+ if (isnew == 0)
11101110+ {
11111111+ global.last_shm_base = malloc(size);
11121112+ if (global.last_shm_base != NULL) memcpy(global.last_shm_base, global.shared_memory_base, size);
11131113+ }
11141114+11151115+ memset(global.shared_memory_base, 0, size);
11161116+ global.shared_memory_refcount = (uint32_t *)malloc(size);
11171117+ if (global.shared_memory_refcount == NULL) return -1;
11181118+11191119+ memset(global.shared_memory_refcount, 0, size);
11201120+11211121+ /* slot 0 is notifyd's pid */
11221122+ global.shared_memory_base[0] = getpid();
11231123+ global.shared_memory_refcount[0] = 1;
11241124+ global.slot_id = 0;
11251125+11261126+ return 0;
11271127+}
11281128+11291129+int
11301130+main(int argc, const char *argv[])
11311131+{
11321132+ dispatch_queue_t main_q;
11331133+ const char *service_name;
11341134+ const char *shm_name;
11351135+ uint32_t i, status;
11361136+ struct rlimit rlim;
11371137+11381138+#if TARGET_IPHONE_SIMULATOR
11391139+ asprintf(&_config_file_path, "%s/private/etc/notify.conf", getenv("SIMULATOR_ROOT"));
11401140+ asprintf(&_debug_log_path, "%s/var/log/notifyd.log", getenv("SIMULATOR_LOG_ROOT"));
11411141+#endif
11421142+11431143+ service_name = NOTIFY_SERVICE_NAME;
11441144+ shm_name = SHM_ID;
11451145+11461146+ notify_set_options(NOTIFY_OPT_DISABLE);
11471147+11481148+ /* remove limit of number of file descriptors */
11491149+ rlim.rlim_max = RLIM_INFINITY;
11501150+ rlim.rlim_cur = MIN(OPEN_MAX, rlim.rlim_max);
11511151+ setrlimit(RLIMIT_NOFILE, &rlim);
11521152+11531153+ signal(SIGPIPE, SIG_IGN);
11541154+ signal(SIGHUP, SIG_IGN);
11551155+ signal(SIGUSR1, SIG_IGN);
11561156+ signal(SIGUSR2, SIG_IGN);
11571157+ signal(SIGWINCH, SIG_IGN);
11581158+11591159+ memset(&call_statistics, 0, sizeof(struct call_statistics_s));
11601160+11611161+ global.request_size = sizeof(notify_request_msg) + MAX_TRAILER_SIZE;
11621162+ global.reply_size = sizeof(notify_reply_msg) + MAX_TRAILER_SIZE;
11631163+ global.nslots = getpagesize() / sizeof(uint32_t);
11641164+ global.notify_state = _notify_lib_notify_state_new(NOTIFY_STATE_ENABLE_RESEND, 1024);
11651165+ global.log_cutoff = ASL_LEVEL_ERR;
11661166+ global.log_path = strdup(DEBUG_LOG_PATH);
11671167+ global.slot_id = (uint32_t)-1;
11681168+11691169+ for (i = 1; i < argc; i++)
11701170+ {
11711171+ if (!strcmp(argv[i], "-d"))
11721172+ {
11731173+ global.log_cutoff = ASL_LEVEL_DEBUG;
11741174+ }
11751175+ else if (!strcmp(argv[i], "-log_cutoff"))
11761176+ {
11771177+ global.log_cutoff = atoi(argv[++i]);
11781178+ }
11791179+ else if (!strcmp(argv[i], "-log_file"))
11801180+ {
11811181+ free(global.log_path);
11821182+ global.log_path = strdup(argv[++i]);
11831183+ }
11841184+ else if (!strcmp(argv[i], "-service"))
11851185+ {
11861186+ service_name = argv[++i];
11871187+ }
11881188+ else if (!strcmp(argv[i], "-shm"))
11891189+ {
11901190+ shm_name = argv[++i];
11911191+ }
11921192+ else if (!strcmp(argv[i], "-shm_pages"))
11931193+ {
11941194+ global.nslots = atoi(argv[++i]) * (getpagesize() / sizeof(uint32_t));
11951195+ }
11961196+ }
11971197+11981198+ global.log_default = global.log_cutoff;
11991199+12001200+ log_message(ASL_LEVEL_DEBUG, "--------------------\nnotifyd start PID %u\n", getpid());
12011201+12021202+ init_launch_config(service_name);
12031203+12041204+ if (global.nslots > 0)
12051205+ {
12061206+ status = open_shared_memory(shm_name);
12071207+ assert(status == 0);
12081208+ }
12091209+12101210+ /* init from config file before starting the work queue */
12111211+ init_config();
12121212+12131213+ main_q = dispatch_get_main_queue();
12141214+ assert(main_q != NULL);
12151215+12161216+ global.work_q = dispatch_queue_create("WorkQ", NULL);
12171217+ assert(global.work_q != NULL);
12181218+12191219+#if TARGET_OS_EMBEDDED
12201220+ /* Block a thread in mach_msg() to avoid the syscall overhead of frequent
12211221+ * dispatch source wakeup, and synchronize with work_q after message
12221222+ * reception in service_mach_message(). <rdar://problem/8785140> */
12231223+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
12241224+ forever service_mach_message(true);
12251225+ });
12261226+#else
12271227+ global.mach_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, global.server_port, 0, global.work_q);
12281228+ assert(global.mach_src != NULL);
12291229+12301230+ dispatch_source_set_event_handler(global.mach_src, ^{
12311231+ service_mach_message(false);
12321232+ });
12331233+ dispatch_resume(global.mach_src);
12341234+#endif
12351235+12361236+ /* Set up SIGUSR1 */
12371237+ global.sig_usr1_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGUSR1, 0, main_q);
12381238+ assert(global.sig_usr1_src != NULL);
12391239+ dispatch_source_set_event_handler(global.sig_usr1_src, ^{
12401240+ dispatch_async(global.work_q, ^{ dump_status(STATUS_REQUEST_SHORT); });
12411241+ });
12421242+ dispatch_resume(global.sig_usr1_src);
12431243+12441244+ /* Set up SIGUSR2 */
12451245+ global.sig_usr2_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGUSR2, 0, main_q);
12461246+ assert(global.sig_usr2_src != NULL);
12471247+ dispatch_source_set_event_handler(global.sig_usr2_src, ^{
12481248+ dispatch_async(global.work_q, ^{ dump_status(STATUS_REQUEST_LONG); });
12491249+ });
12501250+ dispatch_resume(global.sig_usr2_src);
12511251+12521252+ /* Set up SIGWINCH */
12531253+ global.sig_winch_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGWINCH, 0, main_q);
12541254+ assert(global.sig_winch_src != NULL);
12551255+ dispatch_source_set_event_handler(global.sig_winch_src, ^{
12561256+ if (global.log_cutoff == ASL_LEVEL_DEBUG) global.log_cutoff = global.log_default;
12571257+ else global.log_cutoff = ASL_LEVEL_DEBUG;
12581258+ });
12591259+ dispatch_resume(global.sig_winch_src);
12601260+12611261+ dispatch_main();
12621262+12631263+ /* NOTREACHED */
12641264+ return 0;
12651265+}
+101
libnotify/notifyd/notifyd.h
···11+/*
22+ * Copyright (c) 2003-2010 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#ifndef _NOTIFY_DAEMON_H_
2525+#define _NOTIFY_DAEMON_H_
2626+2727+#include <libnotify.h>
2828+#include <mach/mach.h>
2929+#include <launch.h>
3030+#include <dispatch/dispatch.h>
3131+3232+#define NOTIFY_IPC_VERSION 2
3333+3434+struct global_s
3535+{
3636+ mach_port_t server_port;
3737+ launch_data_t launch_dict;
3838+ notify_state_t *notify_state;
3939+ dispatch_queue_t work_q;
4040+ dispatch_source_t mach_src;
4141+ dispatch_source_t sig_usr1_src;
4242+ dispatch_source_t sig_usr2_src;
4343+ dispatch_source_t sig_winch_src;
4444+ uint32_t request_size;
4545+ uint32_t reply_size;
4646+ uint32_t nslots;
4747+ uint32_t slot_id;
4848+ uint32_t *shared_memory_base;
4949+ uint32_t *shared_memory_refcount;
5050+ uint32_t *last_shm_base;
5151+ uint32_t log_cutoff;
5252+ uint32_t log_default;
5353+ char *log_path;
5454+} global;
5555+5656+struct call_statistics_s
5757+{
5858+ uint64_t post;
5959+ uint64_t post_no_op;
6060+ uint64_t post_by_id;
6161+ uint64_t post_by_name;
6262+ uint64_t post_by_name_and_fetch_id;
6363+ uint64_t reg;
6464+ uint64_t reg_plain;
6565+ uint64_t reg_check;
6666+ uint64_t reg_signal;
6767+ uint64_t reg_file;
6868+ uint64_t reg_port;
6969+ uint64_t cancel;
7070+ uint64_t suspend;
7171+ uint64_t resume;
7272+ uint64_t suspend_pid;
7373+ uint64_t resume_pid;
7474+ uint64_t check;
7575+ uint64_t get_state;
7676+ uint64_t get_state_by_client;
7777+ uint64_t get_state_by_id;
7878+ uint64_t get_state_by_client_and_fetch_id;
7979+ uint64_t set_state;
8080+ uint64_t set_state_by_client;
8181+ uint64_t set_state_by_id;
8282+ uint64_t set_state_by_client_and_fetch_id;
8383+ uint64_t get_owner;
8484+ uint64_t set_owner;
8585+ uint64_t get_access;
8686+ uint64_t set_access;
8787+ uint64_t monitor_file;
8888+ uint64_t service_timer;
8989+ uint64_t service_path;
9090+ uint64_t cleanup;
9191+ uint64_t regenerate;
9292+} call_statistics;
9393+9494+extern void log_message(int priority, const char *str, ...);
9595+extern uint32_t daemon_post(const char *name, uint32_t u, uint32_t g);
9696+extern uint32_t daemon_post_nid(uint64_t nid, uint32_t u, uint32_t g);
9797+extern void daemon_post_client(uint64_t cid);
9898+extern void daemon_set_state(const char *name, uint64_t val);
9999+extern void dump_status(uint32_t level);
100100+101101+#endif /* _NOTIFY_DAEMON_H_ */
+953
libnotify/notifyd/pathwatch.c
···11+/*
22+ * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+/*
2525+ * These routines, accessed through path_node_create() and path_node_release(),
2626+ * provide an API for monitoring a path in the filesystem. The path may contain
2727+ * directories and symbolic links. The path may not even exist! If the path
2828+ * does exist, this code will respond to path deletions, component renaming, and
2929+ * access control changes that either delete the path or make it inaccessible to a
3030+ * target user/group. A notification will be provided if the path comes back into
3131+ * existance or again becomes accessible.
3232+ *
3333+ * path_node_create() returns a path_node_t object, which contains a dispatch_source_t.
3434+ * This source behaves very much like a DISPATCH_SOURCE_TYPE_VNODE, except that it also
3535+ * triggers on the creation of a path.
3636+ *
3737+ * Internally, the work of monitoring a path is done by a set of helper vnode_t
3838+ * objects. A vnode_t contains a dispatch_source_t (of type DISPATCH_SOURCE_TYPE_VNODE)
3939+ * for a particular vnode. When a path_node_t is created, it creates (or shares)
4040+ * vnode_t objects for each component of the desired path. For example, a path_node_t
4141+ * for "/a/b/c" will create (or share, if some other path_node_t has already created) a
4242+ * dispatch_source_t for "/", "/a", "/a/b", and "/a/b/c". If any of these sources is
4343+ * notified of a change, the vnode_t will trigger an update for all path_node_t
4444+ * objects that contain that path component.
4545+ *
4646+ * When a path_node_t update is triggered by a vnode_t component, the node re-evaluates
4747+ * the target path that it is charged with monitoring. If the path exists and the end-point
4848+ * vnode changed, then the update operation will trigger its dispatch_source_t to notify the
4949+ * end-user of the change. If an intermediate path component is removed, renamed, or becomes
5050+ * blocked by an access-control change, then the end-point dispatch_source_t is triggered to
5151+ * indicate that the path has been deleted. However, the path_node_t remains active and
5252+ * monitors the path components that still exist. Eventually, if the path is recreated or
5353+ * if access controls change so that the path becomes visible to the target user, then the
5454+ * end-point dispatch_source_t is triggered with a PATH_NODE_CREATE bit set in its data flags.
5555+ *
5656+ * path_node_releases() releases a path_node_t object and all of the vnode_t objects
5757+ * that were monitoring components of its target path.
5858+ */
5959+6060+#include <stdio.h>
6161+#include <stdlib.h>
6262+#include <string.h>
6363+#include <errno.h>
6464+#include <sys/stat.h>
6565+#include <sys/param.h>
6666+#include <sys/syscall.h>
6767+#include <sys/kauth.h>
6868+#include <pwd.h>
6969+#include <fcntl.h>
7070+#include <assert.h>
7171+#include <tzfile.h>
7272+#include "pathwatch.h"
7373+7474+#define forever for(;;)
7575+#define streq(A,B) (strcmp(A,B)==0)
7676+#define DISPATCH_VNODE_ALL 0x7f
7777+7878+#define PATH_STAT_OK 0
7979+#define PATH_STAT_FAILED 1
8080+#define PATH_STAT_ACCESS 2
8181+8282+#define VPATH_NODE_TYPE_REG 0
8383+#define VPATH_NODE_TYPE_LINK 1
8484+#define VPATH_NODE_TYPE_DELETED 2
8585+8686+#define DISPATCH_VNODE_UNAVAIL (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE)
8787+8888+/* Libinfo global */
8989+extern uint32_t gL1CacheEnabled;
9090+9191+/*
9292+ * vnode_t represents a vnode.
9393+ *
9494+ * The dispatch source is of type DISPATCH_SOURCE_TYPE_VNODE for file descriptor fd.
9595+ * The handler for the source triggers an update routine for all the path_node_t
9696+ * objects in the path_node list.
9797+ */
9898+typedef struct
9999+{
100100+ char *path;
101101+ uint32_t type;
102102+ int fd;
103103+ struct timespec mtime;
104104+ struct timespec ctime;
105105+ dispatch_source_t src;
106106+ uint32_t path_node_count;
107107+ path_node_t **path_node;
108108+} vnode_t;
109109+110110+static struct
111111+{
112112+ dispatch_once_t pathwatch_init;
113113+ dispatch_queue_t pathwatch_queue;
114114+ uint32_t vnode_count;
115115+ vnode_t **vnode;
116116+ char *tzdir;
117117+ size_t tzdir_len;
118118+} _global = {0};
119119+120120+/* forward */
121121+static void _path_node_update(path_node_t *pnode, uint32_t flags, vnode_t *vnode);
122122+123123+/*
124124+ * stat() or lstat() a path as a particular user/group.
125125+ */
126126+static int
127127+_path_stat(const char *path, int link, uid_t uid, gid_t gid)
128128+{
129129+ struct stat sb;
130130+ gid_t orig_gidset[NGROUPS_MAX];
131131+ int ngroups, status, stat_status;
132132+ struct passwd *p;
133133+ uint32_t orig_cache_enabled;
134134+135135+ /* disable L1 cache to avoid notification deadlock */
136136+ orig_cache_enabled = gL1CacheEnabled;
137137+ gL1CacheEnabled = 0;
138138+139139+ /* get my group list */
140140+ memset(orig_gidset, 0, sizeof(orig_gidset));
141141+ ngroups = getgroups(NGROUPS_MAX, orig_gidset);
142142+ if (ngroups < 0)
143143+ {
144144+ return PATH_STAT_FAILED;
145145+ }
146146+147147+ /* look up user name */
148148+ p = getpwuid(uid);
149149+ if (p == NULL)
150150+ {
151151+ gL1CacheEnabled = orig_cache_enabled;
152152+ return PATH_STAT_FAILED;
153153+ }
154154+155155+ /* switch to user's grouplist */
156156+ status = initgroups(p->pw_name, gid);
157157+ if (status < 0)
158158+ {
159159+ gL1CacheEnabled = orig_cache_enabled;
160160+ return PATH_STAT_FAILED;
161161+ }
162162+163163+ /* reset gL1CacheEnabled */
164164+ gL1CacheEnabled = orig_cache_enabled;
165165+166166+ /* set thread credentials */
167167+ pthread_setugid_np(uid, gid);
168168+169169+ /* stat the file */
170170+ stat_status = -1;
171171+ if (link != 0)
172172+ {
173173+ stat_status = lstat(path, &sb);
174174+ }
175175+ else
176176+ {
177177+ stat_status = stat(path, &sb);
178178+ }
179179+180180+ /* unset thread credentials */
181181+ pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE);
182182+183183+ /* restore original grouplist for UID 0 */
184184+ status = syscall(SYS_initgroups, ngroups, orig_gidset, 0);
185185+ if (status < 0)
186186+ {
187187+ return PATH_STAT_FAILED;
188188+ }
189189+190190+ /* return status */
191191+ if (stat_status == 0)
192192+ {
193193+ return PATH_STAT_OK;
194194+ }
195195+196196+ if (errno == EACCES)
197197+ {
198198+ return PATH_STAT_ACCESS;
199199+ }
200200+201201+ return PATH_STAT_FAILED;
202202+}
203203+204204+/*
205205+ * Check access to a path by a particular user/group.
206206+ * Sets ftype output parameter if it is non-NULL.
207207+ */
208208+static int
209209+_path_stat_check_access(const char *path, uid_t uid, gid_t gid, uint32_t *ftype)
210210+{
211211+ struct stat sb;
212212+ char buf[MAXPATHLEN + 1];
213213+ int status, t;
214214+215215+ if (path == NULL) return PATH_STAT_FAILED;
216216+217217+ if (ftype != NULL) *ftype = PATH_NODE_TYPE_GHOST;
218218+219219+ /* Paths must be absolute */
220220+ if (path[0] != '/') return PATH_STAT_FAILED;
221221+222222+ /* Root dir is readable */
223223+ if (path[1] == '\0')
224224+ {
225225+ if (ftype != NULL) *ftype = PATH_NODE_TYPE_DIR;
226226+ return PATH_STAT_OK;
227227+ }
228228+229229+ memset(&sb, 0, sizeof(struct stat));
230230+ status = lstat(path, &sb);
231231+232232+ if (status != 0) return PATH_STAT_FAILED;
233233+ else if ((sb.st_mode & S_IFMT) == S_IFDIR) t = PATH_NODE_TYPE_DIR;
234234+ else if ((sb.st_mode & S_IFMT) == S_IFREG) t = PATH_NODE_TYPE_FILE;
235235+ else if ((sb.st_mode & S_IFMT) == S_IFLNK) t = PATH_NODE_TYPE_LINK;
236236+ else t = PATH_NODE_TYPE_OTHER;
237237+238238+ if (ftype != NULL) *ftype = t;
239239+240240+ if (t == PATH_NODE_TYPE_OTHER) return PATH_STAT_FAILED;
241241+242242+ /* skip access control check if uid is zero */
243243+ if (uid == 0) return 0;
244244+245245+ /* special case: anything in the timezone directory is OK */
246246+ memset(buf, 0, sizeof(buf));
247247+ if (realpath(path, buf) == NULL) return PATH_STAT_FAILED;
248248+ if ((_global.tzdir != NULL) && (!strncasecmp(buf, _global.tzdir, _global.tzdir_len)))
249249+ {
250250+ return PATH_STAT_OK;
251251+ }
252252+253253+ /* call _path_stat to check access as the user/group provided */
254254+ if (t == PATH_NODE_TYPE_FILE)
255255+ {
256256+ status = _path_stat(path, 0, uid, gid);
257257+ if ((status == PATH_STAT_ACCESS) && (ftype != NULL)) *ftype = PATH_NODE_TYPE_GHOST;
258258+ return status;
259259+ }
260260+ else if (t == PATH_NODE_TYPE_LINK)
261261+ {
262262+ status = _path_stat(path, 1, uid, gid);
263263+ if ((status == PATH_STAT_ACCESS) && (ftype != NULL)) *ftype = PATH_NODE_TYPE_GHOST;
264264+ return status;
265265+ }
266266+ else if (t == PATH_NODE_TYPE_DIR)
267267+ {
268268+ snprintf(buf, MAXPATHLEN, "%s/.", path);
269269+ status = _path_stat(buf, 0, uid, gid);
270270+ if ((status == PATH_STAT_ACCESS) && (ftype != NULL)) *ftype = PATH_NODE_TYPE_GHOST;
271271+ return status;
272272+ }
273273+274274+ /* we don't ever get here, but... */
275275+ return PATH_STAT_FAILED;
276276+}
277277+278278+/*
279279+ * Uniquely add a pnode to a vnode's list of path nodes.
280280+ */
281281+static void
282282+_vnode_add_pnode(vnode_t *vnode, path_node_t *pnode)
283283+{
284284+ uint32_t i;
285285+286286+ for (i = 0; i < vnode->path_node_count; i++)
287287+ {
288288+ if (vnode->path_node[i] == pnode) return;
289289+ }
290290+291291+ for (i = 0; i < vnode->path_node_count; i++)
292292+ {
293293+ if (vnode->path_node[i] == NULL)
294294+ {
295295+ vnode->path_node[i] = pnode;
296296+ return;
297297+ }
298298+ }
299299+300300+ if (vnode->path_node_count == 0)
301301+ {
302302+ vnode->path_node = (path_node_t **)calloc(1, sizeof(path_node_t *));
303303+ }
304304+ else
305305+ {
306306+ vnode->path_node = (path_node_t **)reallocf(vnode->path_node, (vnode->path_node_count + 1) * sizeof(path_node_t *));
307307+ }
308308+309309+ assert(vnode->path_node != NULL);
310310+311311+ vnode->path_node[vnode->path_node_count++] = pnode;
312312+}
313313+314314+/*
315315+ * Free a vnode_t and cancel/release its dispatch source.
316316+ */
317317+static void
318318+_vnode_free(vnode_t *vnode)
319319+{
320320+ dispatch_source_cancel(vnode->src);
321321+322322+ /*
323323+ * Actually free the vnode on the pathwatch queue. This allows any
324324+ * enqueued _vnode_event operations to complete before the vnode disappears.
325325+ * _vnode_event() quietly returns if the source has been cancelled.
326326+ */
327327+ dispatch_async(_global.pathwatch_queue, ^{
328328+ dispatch_release(vnode->src);
329329+ free(vnode->path);
330330+ free(vnode->path_node);
331331+ free(vnode);
332332+ });
333333+}
334334+335335+/*
336336+ * Handler routine for vnode_t objects.
337337+ * Invokes the _path_node_update routine for all of the vnode's pnodes.
338338+ */
339339+static void
340340+_vnode_event(vnode_t *vnode)
341341+{
342342+ uint32_t i, flags;
343343+ unsigned long ulf;
344344+ struct stat sb;
345345+346346+ if (vnode == NULL) return;
347347+ if ((vnode->src != NULL) && (dispatch_source_testcancel(vnode->src))) return;
348348+349349+ ulf = dispatch_source_get_data(vnode->src);
350350+ flags = ulf;
351351+352352+ memset(&sb, 0, sizeof(struct stat));
353353+ if (fstat(vnode->fd, &sb) == 0)
354354+ {
355355+ if ((vnode->mtime.tv_sec != sb.st_mtimespec.tv_sec) || (vnode->mtime.tv_nsec != sb.st_mtimespec.tv_nsec))
356356+ {
357357+ flags |= PATH_NODE_MTIME;
358358+ vnode->mtime = sb.st_mtimespec;
359359+ }
360360+361361+ if ((vnode->ctime.tv_sec != sb.st_ctimespec.tv_sec) || (vnode->ctime.tv_nsec != sb.st_ctimespec.tv_nsec))
362362+ {
363363+ flags |= PATH_NODE_CTIME;
364364+ vnode->ctime = sb.st_ctimespec;
365365+ }
366366+ }
367367+368368+ /*
369369+ * Flag deleted sources.
370370+ * We can't delete them here, since _path_node_update may need them.
371371+ * However, _path_node_update will release them and they will get cleaned
372372+ * up in a _vnode_sweep later on.
373373+ */
374374+ if (flags & DISPATCH_VNODE_DELETE) vnode->type = VPATH_NODE_TYPE_DELETED;
375375+376376+ for (i = 0; i < vnode->path_node_count; i++)
377377+ {
378378+ _path_node_update(vnode->path_node[i], flags, vnode);
379379+ }
380380+}
381381+382382+/*
383383+ * Creates a vnode_t object.
384384+ */
385385+static vnode_t *
386386+_vnode_create(const char *path, uint32_t type, path_node_t *pnode)
387387+{
388388+ int fd, flags;
389389+ uint32_t i;
390390+ vnode_t *vnode;
391391+ dispatch_source_t src;
392392+ struct stat sb;
393393+394394+ if (path == NULL) path = "/";
395395+ if (path[0] == '\0') path = "/";
396396+397397+ for (i = 0; i < _global.vnode_count; i++)
398398+ {
399399+ vnode = _global.vnode[i];
400400+ if (vnode == NULL) continue;
401401+402402+ if ((vnode->type == type) && (streq(path, vnode->path)))
403403+ {
404404+ _vnode_add_pnode(vnode, pnode);
405405+ return vnode;
406406+ }
407407+ }
408408+409409+ vnode = NULL;
410410+411411+ flags = O_EVTONLY;
412412+ if (type == VPATH_NODE_TYPE_LINK) flags |= O_SYMLINK;
413413+414414+ fd = open(path, flags, 0);
415415+ if (fd < 0) return NULL;
416416+417417+ src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, (uintptr_t)fd, DISPATCH_VNODE_ALL, _global.pathwatch_queue);
418418+ if (src == NULL)
419419+ {
420420+ close(fd);
421421+ return NULL;
422422+ }
423423+424424+ vnode = (vnode_t *)calloc(1, sizeof(vnode_t));
425425+ assert(vnode != NULL);
426426+427427+ vnode->type = type;
428428+ vnode->path = strdup(path);
429429+ assert(vnode->path != NULL);
430430+431431+ vnode->fd = fd;
432432+ vnode->src = src;
433433+434434+ memset(&sb, 0, sizeof(struct stat));
435435+ if (fstat(fd, &sb) == 0)
436436+ {
437437+ vnode->mtime = sb.st_mtimespec;
438438+ vnode->ctime = sb.st_ctimespec;
439439+ }
440440+441441+ _vnode_add_pnode(vnode, pnode);
442442+443443+ dispatch_source_set_event_handler(src, ^{ _vnode_event(vnode); });
444444+ dispatch_source_set_cancel_handler(src, ^{ close(fd); });
445445+446446+ if (_global.vnode_count == 0)
447447+ {
448448+ _global.vnode = (vnode_t **)calloc(1, sizeof(vnode_t *));
449449+ }
450450+ else
451451+ {
452452+ _global.vnode = (vnode_t **)reallocf(_global.vnode, (_global.vnode_count + 1) * sizeof(vnode_t *));
453453+ }
454454+455455+ assert(_global.vnode != NULL);
456456+457457+ _global.vnode[_global.vnode_count++] = vnode;
458458+459459+ dispatch_resume(src);
460460+461461+ return vnode;
462462+}
463463+464464+static vnode_t *
465465+_vnode_create_real_path(const char *path, uint32_t type, path_node_t *pnode)
466466+{
467467+ char real[MAXPATHLEN + 1];
468468+469469+ if (path == NULL) return _vnode_create(path, type, pnode);
470470+471471+ if (NULL != realpath(path, real)) return _vnode_create(real, type, pnode);
472472+473473+ return NULL;
474474+}
475475+476476+/*
477477+ * Examines all the vnode_t objects (held in the _global data),
478478+ * frees any that have no path nodes.
479479+ */
480480+static void
481481+_vnode_sweep()
482482+{
483483+ uint32_t i, j, new_vnode_count, new_path_node_count;
484484+ vnode_t **new_source, *vnode;
485485+ path_node_t **new_path_node;
486486+487487+ new_source = NULL;
488488+489489+ for (i = 0; i < _global.vnode_count; i++)
490490+ {
491491+ vnode = _global.vnode[i];
492492+ if (vnode == NULL) continue;
493493+494494+ new_path_node_count = 0;
495495+ new_path_node = NULL;
496496+497497+ for (j = 0; j < vnode->path_node_count; j++)
498498+ {
499499+ if (vnode->path_node[j] != NULL) new_path_node_count++;
500500+ }
501501+502502+ if (new_path_node_count == vnode->path_node_count)
503503+ {
504504+ /* no change */
505505+ continue;
506506+ }
507507+ else if (new_path_node_count > 0)
508508+ {
509509+ new_path_node = (path_node_t **)calloc(new_path_node_count, sizeof(path_node_t *));
510510+ assert(new_path_node != NULL);
511511+512512+ new_path_node_count = 0;
513513+ for (j = 0; j < vnode->path_node_count; j++)
514514+ {
515515+ if (vnode->path_node[j] != NULL)
516516+ {
517517+ new_path_node[new_path_node_count++] = vnode->path_node[j];
518518+ }
519519+ }
520520+ }
521521+522522+ free(vnode->path_node);
523523+ vnode->path_node = new_path_node;
524524+ vnode->path_node_count = new_path_node_count;
525525+ }
526526+527527+ new_vnode_count = 0;
528528+ for (i = 0; i < _global.vnode_count; i++)
529529+ {
530530+ vnode = _global.vnode[i];
531531+ if (vnode == NULL) continue;
532532+ if (vnode->path_node_count > 0) new_vnode_count++;
533533+ }
534534+535535+ if (new_vnode_count == _global.vnode_count)
536536+ {
537537+ /* no change */
538538+ return;
539539+ }
540540+ else if (new_vnode_count > 0)
541541+ {
542542+ new_source = (vnode_t **)calloc(new_vnode_count, sizeof(vnode_t *));
543543+ assert(new_source != NULL);
544544+545545+ new_vnode_count = 0;
546546+ for (i = 0; i < _global.vnode_count; i++)
547547+ {
548548+ vnode = _global.vnode[i];
549549+ if (vnode == NULL) continue;
550550+551551+ if (vnode->path_node_count > 0)
552552+ {
553553+ new_source[new_vnode_count++] = vnode;
554554+ }
555555+ else
556556+ {
557557+ _vnode_free(vnode);
558558+ }
559559+ }
560560+ }
561561+562562+ free(_global.vnode);
563563+ _global.vnode = new_source;
564564+ _global.vnode_count = new_vnode_count;
565565+}
566566+567567+/*
568568+ * Releases sources that have a particular node on their list.
569569+ * This is a deferred release mechanism for vnode_t objects.
570570+ * The calling routine must call _vnode_sweep subsequent to
571571+ * calling this routine.
572572+ * _vnode_sweep will actually free any vnode_t objects
573573+ * that have a no path nodes.
574574+ */
575575+static void
576576+_vnode_release_for_node(path_node_t *pnode)
577577+{
578578+ uint32_t i, j;
579579+ vnode_t *vnode;
580580+581581+ for (i = 0; i < _global.vnode_count; i++)
582582+ {
583583+ vnode = _global.vnode[i];
584584+ if (vnode == NULL) continue;
585585+586586+ for (j = 0; j < vnode->path_node_count; j++)
587587+ {
588588+ if (vnode->path_node[j] == pnode)
589589+ {
590590+ vnode->path_node[j] = NULL;
591591+ break;
592592+ }
593593+ }
594594+ }
595595+}
596596+597597+/*
598598+ * Retain a path_node_t object.
599599+ * Dispatched on _global.pathwatch_queue.
600600+ */
601601+static void
602602+_path_node_retain(path_node_t *pnode)
603603+{
604604+ if (pnode == NULL) return;
605605+ pnode->refcount++;
606606+}
607607+608608+/*
609609+ * Free a path_node_t object.
610610+ * Dispatched on _global.pathwatch_queue.
611611+ */
612612+static void
613613+_path_node_free(path_node_t *pnode)
614614+{
615615+ uint32_t i, n;
616616+617617+ if (pnode == NULL) return;
618618+619619+ /*
620620+ * Remove this path node from all vnodes.
621621+ */
622622+ _vnode_release_for_node(pnode);
623623+ _vnode_sweep();
624624+625625+ free(pnode->path);
626626+627627+ if (pnode->pname != NULL)
628628+ {
629629+ n = pnode->pname_count;
630630+ pnode->pname_count = 0;
631631+632632+ for (i = 0; i < n; i++)
633633+ {
634634+ free(pnode->pname[i]);
635635+ pnode->pname[i] = NULL;
636636+ }
637637+638638+ free(pnode->pname);
639639+ }
640640+641641+ free(pnode->contextp);
642642+643643+ dispatch_release(pnode->src);
644644+ dispatch_release(pnode->src_queue);
645645+646646+ memset(pnode, 0, sizeof(path_node_t));
647647+ free(pnode);
648648+}
649649+650650+/*
651651+ * Release a path_node_t object.
652652+ */
653653+static void
654654+_path_node_release(path_node_t *pnode)
655655+{
656656+ if (pnode == NULL) return;
657657+658658+ /*
659659+ * We need to make sure that the node's event handler isn't currently
660660+ * executing before freeing the node. We dispatch on the src_queue, so
661661+ * that when the block executes there will be no more events in the queue.
662662+ * From there, we dispatch async back to the pathwatch_queue to do the
663663+ * data structure cleanup.
664664+ */
665665+ dispatch_async(pnode->src_queue, ^{
666666+ dispatch_async(_global.pathwatch_queue, ^{
667667+ if (pnode->refcount > 0) pnode->refcount--;
668668+ if (pnode->refcount == 0) _path_node_free(pnode);
669669+ });
670670+ });
671671+}
672672+673673+/*
674674+ * Frees a path_node_t object.
675675+ * The work is actually done on the global pathwatch_queue to make this safe.
676676+ */
677677+void
678678+path_node_close(path_node_t *pnode)
679679+{
680680+ if (pnode == NULL) return;
681681+682682+ if (pnode->src != NULL) dispatch_source_cancel(pnode->src);
683683+ _path_node_release(pnode);
684684+}
685685+686686+static void
687687+_pathwatch_init()
688688+{
689689+ char buf[MAXPATHLEN];
690690+691691+ /* Create serial queue for node creation / deletion operations */
692692+ _global.pathwatch_queue = dispatch_queue_create("pathwatch", NULL);
693693+694694+ _global.tzdir = NULL;
695695+ _global.tzdir_len = 0;
696696+697697+ /* Get the real path to TZDIR */
698698+ if (realpath(TZDIR, buf) != NULL)
699699+ {
700700+ _global.tzdir_len = strlen(buf);
701701+ _global.tzdir = strdup(buf);
702702+ if (_global.tzdir == NULL) _global.tzdir_len = 0;
703703+ }
704704+}
705705+706706+/*
707707+ * _path_node_init is responsible for allocating a path_node_t structure,
708708+ * and for creating the pname array and setting the path component.
709709+ * The path is a sanatized version of the caller's path with redundant "/"
710710+ * characters stripped out. The pname array contains each "/" separated
711711+ * component of the path.
712712+ *
713713+ * For example, _path_node_init("///foo////bar//baz/") creates:
714714+ * pnode->path = "/foo/bar/baz"
715715+ * pnode->pname_count = 3
716716+ * pnode->pname[0] = "foo"
717717+ * pnode->pname[1] = "bar"
718718+ * pnode->pname[2] = "baz"
719719+ */
720720+static path_node_t *
721721+_path_node_init(const char *path)
722722+{
723723+ size_t len;
724724+ uint32_t i;
725725+ path_node_t *pnode;
726726+ const char *start, *end;
727727+ char *name;
728728+729729+ if (path == NULL) path = "/";
730730+ if (path[0] != '/') return NULL;
731731+732732+ pnode = (path_node_t *)calloc(1, sizeof(path_node_t));
733733+ assert(pnode != NULL);
734734+735735+ pnode->plen = 1;
736736+ start = path;
737737+ while (*start == '/') start++;
738738+739739+ forever
740740+ {
741741+ end = strchr(start, '/');
742742+ if (end == NULL) end = strchr(start, '\0');
743743+744744+ len = end - start;
745745+ if (len == 0) break;
746746+747747+ pnode->plen += (len + 1);
748748+749749+ name = NULL;
750750+ if (end == NULL)
751751+ {
752752+ name = strdup(start);
753753+ }
754754+ else
755755+ {
756756+ name = malloc(len + 1);
757757+ assert(name != NULL);
758758+ strncpy(name, start, len);
759759+ name[len] = '\0';
760760+ }
761761+762762+ if (pnode->pname_count == 0)
763763+ {
764764+ pnode->pname = (char **)calloc(1, sizeof(char *));
765765+ }
766766+ else
767767+ {
768768+ pnode->pname = (char **)reallocf(pnode->pname, (pnode->pname_count + 1) * sizeof(char *));
769769+ }
770770+771771+ assert(pnode->pname != NULL);
772772+ pnode->pname[pnode->pname_count] = name;
773773+ pnode->pname_count++;
774774+775775+ start = end;
776776+ if (start != NULL)
777777+ {
778778+ /* skip '/' chars */
779779+ while (*start == '/') start++;
780780+ }
781781+ }
782782+783783+ pnode->path = calloc(1, pnode->plen);
784784+ assert(pnode->path != NULL);
785785+ /*
786786+ * Reconstruct the path here to strip out excess "/" chars.
787787+ * This ensures that path comparisons in _path_node_update are correct.
788788+ */
789789+ for (i = 0; i < pnode->pname_count; i++)
790790+ {
791791+ strlcat(pnode->path, "/", pnode->plen);
792792+ strlcat(pnode->path, pnode->pname[i], pnode->plen);
793793+ }
794794+795795+ return pnode;
796796+}
797797+798798+/* dispatched on _global.pathwatch_queue */
799799+static void
800800+_path_node_update(path_node_t *pnode, uint32_t flags, vnode_t *vnode)
801801+{
802802+ char *buf, fixed[MAXPATHLEN + 1];
803803+ uint32_t i, old_type;
804804+ int status;
805805+ unsigned long data;
806806+ struct stat sb;
807807+808808+ if (pnode == NULL) return;
809809+ if ((pnode->src != NULL) && (dispatch_source_testcancel(pnode->src))) return;
810810+811811+ old_type = pnode->type;
812812+813813+ status = _path_stat_check_access(pnode->path, pnode->uid, pnode->gid, &(pnode->type));
814814+ if (status == PATH_STAT_ACCESS) flags |= DISPATCH_VNODE_REVOKE;
815815+816816+ data = 0;
817817+818818+ if (vnode != NULL)
819819+ {
820820+ /* update status */
821821+822822+ if (flags & DISPATCH_VNODE_UNAVAIL)
823823+ {
824824+ pnode->type = PATH_NODE_TYPE_GHOST;
825825+ data |= (flags & DISPATCH_VNODE_UNAVAIL);
826826+ data |= DISPATCH_VNODE_DELETE;
827827+ }
828828+829829+ if ((vnode->path != NULL) && (pnode->path != NULL) && streq(vnode->path, pnode->path))
830830+ {
831831+ /* this is the target VNODE - transfer flags to src data */
832832+ data |= flags;
833833+ }
834834+835835+ if (old_type == PATH_NODE_TYPE_GHOST)
836836+ {
837837+ /* transition from ghost to non-ghost */
838838+ if (pnode->type != PATH_NODE_TYPE_GHOST)
839839+ {
840840+ data |= PATH_NODE_CREATE;
841841+ }
842842+ else
843843+ {
844844+ data = 0;
845845+ }
846846+ }
847847+ else if (pnode->type == PATH_NODE_TYPE_GHOST)
848848+ {
849849+ /* transition from non-ghost to ghost */
850850+ data |= PATH_NODE_DELETE;
851851+ }
852852+853853+ data &= (pnode->flags & PATH_NODE_ALL);
854854+ if (data != 0)
855855+ {
856856+ if ((pnode->flags & PATH_SRC_SUSPENDED) == 0)
857857+ {
858858+ /* suspend pnode->src, and fire it after PNODE_COALESCE_TIME */
859859+ pnode->flags |= PATH_SRC_SUSPENDED;
860860+ dispatch_suspend(pnode->src);
861861+862862+ dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, PNODE_COALESCE_TIME);
863863+ _path_node_retain(pnode);
864864+865865+ dispatch_after(delay, _global.pathwatch_queue, ^{
866866+ pnode->flags &= ~PATH_SRC_SUSPENDED;
867867+ dispatch_resume(pnode->src);
868868+ _path_node_release(pnode);
869869+ });
870870+ }
871871+872872+ dispatch_source_merge_data(pnode->src, data);
873873+ }
874874+ }
875875+876876+ buf = NULL;
877877+ if (pnode->plen < MAXPATHLEN) buf = fixed;
878878+ else buf = malloc(pnode->plen);
879879+ assert(buf != NULL);
880880+881881+ /* "autorelease" current sources (_vnode_sweep() will delete those with zero refcount) */
882882+ _vnode_release_for_node(pnode);
883883+884884+ /* create new sources (may re-use existing sources) */
885885+ _vnode_create(NULL, 0, pnode);
886886+887887+ memset(buf, 0, pnode->plen);
888888+ for (i = 0; i < pnode->pname_count; i++)
889889+ {
890890+ assert((strlen(buf) + 1) <= pnode->plen);
891891+ strlcat(buf, "/", pnode->plen);
892892+893893+ assert(pnode->pname[i] != NULL);
894894+ assert((strlen(buf) + strlen(pnode->pname[i])) <= pnode->plen);
895895+ strlcat(buf, pnode->pname[i], pnode->plen);
896896+897897+ memset(&sb, 0, sizeof(struct stat));
898898+ if (lstat(buf, &sb) < 0)
899899+ {
900900+ /* the path stops existing here */
901901+ break;
902902+ }
903903+904904+ if ((sb.st_mode & S_IFMT) == S_IFLNK)
905905+ {
906906+ /* open the symlink itself */
907907+ _vnode_create(buf, VPATH_NODE_TYPE_LINK, pnode);
908908+909909+ /* open the symlink target */
910910+ _vnode_create_real_path(buf, 0, pnode);
911911+ }
912912+ else
913913+ {
914914+ _vnode_create(buf, 0, pnode);
915915+ }
916916+ }
917917+918918+ /* sweep source list (deletes those with zero refcount) */
919919+ _vnode_sweep();
920920+921921+ if (buf != fixed) free(buf);
922922+}
923923+924924+/*
925925+ * Creates a dispatch source that activates when a path changes.
926926+ * Internally, creates a data structure (path_node_t) that represents the entire path.
927927+ * Also creates dispatch sources (vnode_t) for each path component. These vnodes may
928928+ * be shared with other path_node_t structures.
929929+ */
930930+path_node_t *
931931+path_node_create(const char *path, uid_t uid, gid_t gid, uint32_t mask, dispatch_queue_t queue)
932932+{
933933+ path_node_t *pnode;
934934+935935+ dispatch_once(&(_global.pathwatch_init), ^{ _pathwatch_init(); });
936936+937937+ pnode = _path_node_init(path);
938938+ if (pnode == NULL) return NULL;
939939+940940+ pnode->refcount = 1;
941941+ pnode->uid = uid;
942942+ pnode->gid = gid;
943943+944944+ dispatch_sync(_global.pathwatch_queue, ^{ _path_node_update(pnode, 0, NULL); });
945945+946946+ dispatch_retain(queue);
947947+948948+ pnode->src = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_OR, 0, 0, queue);
949949+ pnode->src_queue = queue;
950950+ pnode->flags = mask & PATH_NODE_ALL;
951951+952952+ return pnode;
953953+}
+84
libnotify/notifyd/pathwatch.h
···11+/*
22+ * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#ifndef _PATHWATCH_H_
2525+#define _PATHWATCH_H_
2626+2727+#include <dispatch/dispatch.h>
2828+/*
2929+ * types for virtual path nodes (path_node_t)
3030+ */
3131+#define PATH_NODE_TYPE_GHOST 0
3232+#define PATH_NODE_TYPE_FILE 1
3333+#define PATH_NODE_TYPE_LINK 2
3434+#define PATH_NODE_TYPE_DIR 3
3535+#define PATH_NODE_TYPE_OTHER 4
3636+3737+3838+enum
3939+{
4040+ PATH_NODE_DELETE = 0x0001, /* node or path deleted */
4141+ PATH_NODE_WRITE = 0x0002, /* node written */
4242+ PATH_NODE_EXTEND = 0x0004, /* node extended */
4343+ PATH_NODE_ATTRIB = 0x0008, /* node attributes changed (mtime or ctime) */
4444+ PATH_NODE_LINK = 0x0010, /* node link count changed */
4545+ PATH_NODE_RENAME = 0x0020, /* node renamed, always accompanied by PATH_NODE_DELETE */
4646+ PATH_NODE_REVOKE = 0x0040, /* access revoked, always accompanied by PATH_NODE_DELETE */
4747+ PATH_NODE_CREATE = 0x0080, /* path created or access re-acquired */
4848+ PATH_NODE_MTIME = 0x0100, /* path mtime changed, always accompanied by PATH_NODE_ATTRIB */
4949+ PATH_NODE_CTIME = 0x0200 /* path ctime changed, always accompanied by PATH_NODE_ATTRIB */
5050+};
5151+5252+/* all bits mask */
5353+#define PATH_NODE_ALL 0x000003ff
5454+/* src is suspended */
5555+#define PATH_SRC_SUSPENDED 0x10000000
5656+5757+/* Path changes coalesce for 100 milliseconds */
5858+#define PNODE_COALESCE_TIME 100000000
5959+6060+/*
6161+ * path_node_t represents a virtual path
6262+ */
6363+typedef struct
6464+{
6565+ char *path;
6666+ size_t plen;
6767+ uid_t uid;
6868+ gid_t gid;
6969+ uint32_t pname_count;
7070+ char **pname;
7171+ uint32_t type;
7272+ uint32_t flags;
7373+ dispatch_source_t src;
7474+ dispatch_queue_t src_queue;
7575+ void *contextp;
7676+ uint32_t context32;
7777+ uint64_t context64;
7878+ uint32_t refcount;
7979+} path_node_t;
8080+8181+path_node_t *path_node_create(const char *path, uid_t uid, gid_t gid, uint32_t mask, dispatch_queue_t queue);
8282+void path_node_close(path_node_t *pnode);
8383+8484+#endif /* _PATHWATCH_H_ */
+523
libnotify/notifyd/service.c
···11+/*
22+ * Copyright (c) 2003-2010 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#include <stdio.h>
2525+#include <stdlib.h>
2626+#include <string.h>
2727+#include <unistd.h>
2828+#include <assert.h>
2929+#include <asl.h>
3030+#include "notify.h"
3131+#include "notifyd.h"
3232+#include "service.h"
3333+#include "pathwatch.h"
3434+#include "timer.h"
3535+3636+#define NOTIFY_PATH_SERVICE "path:"
3737+#define NOTIFY_PATH_SERVICE_LEN 5
3838+#define NOTIFY_TIMER_SERVICE "timer:"
3939+#define NOTIFY_TIMER_SERVICE_LEN 6
4040+4141+/* Libinfo global */
4242+extern uint32_t gL1CacheEnabled;
4343+4444+static uint32_t
4545+service_type(const char *name)
4646+{
4747+ uint32_t len;
4848+4949+ len = SERVICE_PREFIX_LEN;
5050+ if (strncmp(name, SERVICE_PREFIX, len)) return SERVICE_TYPE_NONE;
5151+ else if (!strncmp(name + len, NOTIFY_PATH_SERVICE, NOTIFY_PATH_SERVICE_LEN)) return SERVICE_TYPE_PATH_PRIVATE;
5252+ else if (!strncmp(name + len, NOTIFY_TIMER_SERVICE, NOTIFY_TIMER_SERVICE_LEN)) return SERVICE_TYPE_TIMER_PRIVATE;
5353+5454+ return SERVICE_TYPE_NONE;
5555+}
5656+5757+/*
5858+ * Request notifications for changes on a filesystem path.
5959+ * This creates a new pathwatch node and sets it to post notifications for
6060+ * the specified name.
6161+ *
6262+ * If the notify name already has a pathwatch node for this path, this routine
6363+ * does nothing and allows the client to piggypack on the existing path watcher.
6464+ *
6565+ * Note that this routine is only called for path monitoring as directed by
6666+ * a "monitor" command in /etc/notify.conf, so only an admin can set up a path
6767+ * that gets public notifications. A client being serviced by the server-side
6868+ * routines in notify_proc.c will only be able to register for a private
6969+ * (per-client) notification for a path. This prevents a client from
7070+ * piggybacking on another client's notifications, and thus prevents the client
7171+ * from getting notifications for a path to which they don't have access.
7272+ */
7373+int
7474+service_open_path(const char *name, const char *path, uid_t uid, gid_t gid)
7575+{
7676+ name_info_t *n;
7777+ svc_info_t *info;
7878+ path_node_t *node;
7979+8080+ call_statistics.service_path++;
8181+8282+ if (path == NULL) return NOTIFY_STATUS_INVALID_REQUEST;
8383+8484+ n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name);
8585+ if (n == NULL) return NOTIFY_STATUS_INVALID_NAME;
8686+8787+ if (n->private != NULL)
8888+ {
8989+ /* a notify key may only have one service associated with it */
9090+ info = (svc_info_t *)n->private;
9191+ if (info->type != SERVICE_TYPE_PATH_PUBLIC) return NOTIFY_STATUS_INVALID_REQUEST;
9292+9393+ /* the client must be asking for the same path that is being monitored */
9494+ node = (path_node_t *)info->private;
9595+ if (strcmp(path, node->path)) return NOTIFY_STATUS_INVALID_REQUEST;
9696+9797+ /* the name is already getting notifications for this path */
9898+ return NOTIFY_STATUS_OK;
9999+ }
100100+101101+ node = path_node_create(path, uid, gid, PATH_NODE_ALL, dispatch_get_main_queue());
102102+ if (node == NULL) return NOTIFY_STATUS_FAILED;
103103+104104+ node->contextp = strdup(name);
105105+106106+ info = (svc_info_t *)calloc(1, sizeof(svc_info_t));
107107+ assert(info != NULL);
108108+109109+ info->type = SERVICE_TYPE_PATH_PUBLIC;
110110+ info->private = node;
111111+ n->private = info;
112112+113113+ dispatch_source_set_event_handler(node->src, ^{
114114+ dispatch_async(global.work_q, ^{
115115+ if (0 == dispatch_source_testcancel(node->src))
116116+ {
117117+ daemon_post((const char *)node->contextp, uid, gid);
118118+ }
119119+ });
120120+ });
121121+122122+ dispatch_resume(node->src);
123123+124124+ return NOTIFY_STATUS_OK;
125125+}
126126+127127+/*
128128+ * The private (per-client) path watch service.
129129+ */
130130+int
131131+service_open_path_private(const char *name, client_t *c, const char *path, uid_t uid, gid_t gid, uint32_t flags)
132132+{
133133+ name_info_t *n;
134134+ svc_info_t *info;
135135+ path_node_t *node;
136136+137137+ call_statistics.service_path++;
138138+139139+ if (path == NULL) return NOTIFY_STATUS_INVALID_REQUEST;
140140+141141+ n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name);
142142+ if (n == NULL) return NOTIFY_STATUS_INVALID_NAME;
143143+ if (c == NULL) return NOTIFY_STATUS_FAILED;
144144+145145+ if (c->private != NULL)
146146+ {
147147+ /* a client may only have one service */
148148+ info = (svc_info_t *)c->private;
149149+ if (info->type != SERVICE_TYPE_PATH_PRIVATE) return NOTIFY_STATUS_INVALID_REQUEST;
150150+151151+ /* the client must be asking for the same path that is being monitored */
152152+ node = (path_node_t *)info->private;
153153+ if (strcmp(path, node->path)) return NOTIFY_STATUS_INVALID_REQUEST;
154154+155155+ /* the client is already getting notifications for this path */
156156+ return NOTIFY_STATUS_OK;
157157+ }
158158+159159+ if (flags == 0) flags = PATH_NODE_ALL;
160160+161161+ node = path_node_create(path, uid, gid, flags, dispatch_get_main_queue());
162162+ if (node == NULL) return NOTIFY_STATUS_FAILED;
163163+164164+ node->context64 = c->client_id;
165165+166166+ info = (svc_info_t *)calloc(1, sizeof(svc_info_t));
167167+ assert(info != NULL);
168168+169169+ info->type = SERVICE_TYPE_PATH_PRIVATE;
170170+ info->private = node;
171171+ c->private = info;
172172+173173+ dispatch_source_set_event_handler(node->src, ^{
174174+ dispatch_async(global.work_q, ^{
175175+ if (0 == dispatch_source_testcancel(node->src))
176176+ {
177177+ daemon_post_client(node->context64);
178178+ }
179179+ });
180180+ });
181181+182182+ dispatch_resume(node->src);
183183+184184+ return NOTIFY_STATUS_OK;
185185+}
186186+187187+/* format: [+]nnnn[s|m|h|d] */
188188+static int
189189+parse_single_arg(const char *arg, int relative_ok, time_t *t)
190190+{
191191+ const char *p, *q;
192192+ time_t now, val;
193193+194194+ if (arg == NULL) return -1;
195195+ p = arg;
196196+197197+ now = 0;
198198+199199+ if ((relative_ok != 0) && ((*p == '+') || (*p == '-')))
200200+ {
201201+ p++;
202202+ now = time(NULL);
203203+ }
204204+205205+ if ((*p < '0') || (*p > '9')) return -1;
206206+207207+ q = strchr(p, '.');
208208+ if (q != NULL) q--;
209209+ else q = arg + strlen(arg) - 1;
210210+211211+#ifdef __LP64__
212212+ val = (time_t)atoll(p);
213213+#else
214214+ val = (time_t)atoi(p);
215215+#endif
216216+217217+ if ((*q >= '0') && (*q <= '9'))
218218+ {}
219219+ else if (*q == 's')
220220+ {}
221221+ else if (*q == 'm')
222222+ {
223223+ val *= 60;
224224+ }
225225+ else if (*q == 'h')
226226+ {
227227+ val *= 3600;
228228+ }
229229+ else if (*q == 'd')
230230+ {
231231+ val *= 86400;
232232+ }
233233+ else
234234+ {
235235+ return -1;
236236+ }
237237+238238+ if (*arg == '-') *t = now - val;
239239+ else *t = now + val;
240240+241241+ return 0;
242242+}
243243+244244+static uint32_t
245245+parse_timer_args(const char *args, time_t *s, time_t *f, time_t *e, int32_t *d)
246246+{
247247+ char *p;
248248+ uint32_t t;
249249+250250+ if (args == NULL) return TIME_EVENT_NONE;
251251+252252+ /* first arg is start time */
253253+ if (parse_single_arg(args, 1, s) != 0) return TIME_EVENT_NONE;
254254+ t = TIME_EVENT_ONESHOT;
255255+256256+ p = strchr(args, '.');
257257+ if (p != NULL)
258258+ {
259259+ /* second arg is frequency */
260260+ p++;
261261+ if (parse_single_arg(p, 0, f) != 0) return TIME_EVENT_NONE;
262262+ t = TIME_EVENT_CLOCK;
263263+264264+ p = strchr(p, '.');
265265+ if (p != NULL)
266266+ {
267267+ /* third arg is end time */
268268+ p++;
269269+ if (parse_single_arg(args, 1, e) != 0) return TIME_EVENT_NONE;
270270+271271+ p = strchr(p, '.');
272272+ if (p != NULL)
273273+ {
274274+ /* fourth arg is day number */
275275+ p++;
276276+ *d = atoi(p);
277277+ t = TIME_EVENT_CAL;
278278+ }
279279+ }
280280+ }
281281+282282+ if (f == 0) t = TIME_EVENT_ONESHOT;
283283+284284+ return t;
285285+}
286286+287287+int
288288+service_open_timer(const char *name, const char *args)
289289+{
290290+ uint32_t t;
291291+ time_t s, f, e;
292292+ int32_t d;
293293+ name_info_t *n;
294294+ svc_info_t *info;
295295+ timer_t *timer;
296296+297297+ call_statistics.service_timer++;
298298+299299+ n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name);
300300+ if (n == NULL) return NOTIFY_STATUS_INVALID_NAME;
301301+302302+ s = f = e = 0;
303303+ d = 0;
304304+305305+ t = parse_timer_args(args, &s, &f, &e, &d);
306306+ if (t == TIME_EVENT_NONE) return NOTIFY_STATUS_INVALID_REQUEST;
307307+308308+ if (n->private != NULL)
309309+ {
310310+ /* a notify key may only have one service associated with it */
311311+ info = (svc_info_t *)n->private;
312312+ if (info->type != SERVICE_TYPE_TIMER_PUBLIC) return NOTIFY_STATUS_INVALID_REQUEST;
313313+314314+ /* the client must be asking for the same timer that is active */
315315+ timer = (timer_t *)info->private;
316316+ if ((timer->type != t) || (timer->start != s) || (timer->freq != f) || (timer->end != e) || (timer->day != d)) return NOTIFY_STATUS_INVALID_REQUEST;
317317+318318+ /* the name is already getting notifications for this timer */
319319+ return NOTIFY_STATUS_OK;
320320+ }
321321+322322+ switch (t)
323323+ {
324324+ case TIME_EVENT_ONESHOT:
325325+ {
326326+ timer = timer_oneshot(s, dispatch_get_main_queue());
327327+ break;
328328+ }
329329+ case TIME_EVENT_CLOCK:
330330+ {
331331+ timer = timer_clock(s, f, e, dispatch_get_main_queue());
332332+ break;
333333+ }
334334+ case TIME_EVENT_CAL:
335335+ {
336336+ timer = timer_calendar(s, f, d, e, dispatch_get_main_queue());
337337+ break;
338338+ }
339339+ default:
340340+ {
341341+ return NOTIFY_STATUS_FAILED;
342342+ }
343343+ }
344344+345345+ if (timer == NULL) return NOTIFY_STATUS_FAILED;
346346+ timer->contextp = strdup(name);
347347+348348+ info = (svc_info_t *)calloc(1, sizeof(svc_info_t));
349349+ assert(info != NULL);
350350+351351+ info->type = SERVICE_TYPE_TIMER_PUBLIC;
352352+ info->private = timer;
353353+ n->private = info;
354354+355355+ dispatch_source_set_event_handler(timer->src, ^{
356356+ dispatch_async(global.work_q, ^{
357357+ if (0 == dispatch_source_testcancel(timer->src))
358358+ {
359359+ daemon_post((const char *)timer->contextp, 0, 0);
360360+ }
361361+ });
362362+ });
363363+364364+ dispatch_resume(timer->src);
365365+366366+ return NOTIFY_STATUS_OK;
367367+}
368368+369369+int
370370+service_open_timer_private(const char *name, client_t *c, const char *args)
371371+{
372372+ uint32_t t;
373373+ time_t s, f, e;
374374+ int32_t d;
375375+ name_info_t *n;
376376+ svc_info_t *info;
377377+ timer_t *timer;
378378+379379+ call_statistics.service_timer++;
380380+381381+ n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name);
382382+ if (n == NULL) return NOTIFY_STATUS_INVALID_NAME;
383383+ if (c == NULL) return NOTIFY_STATUS_FAILED;
384384+385385+ s = f = e = 0;
386386+ d = 0;
387387+388388+ t = parse_timer_args(args, &s, &f, &e, &d);
389389+ if (t == TIME_EVENT_NONE) return NOTIFY_STATUS_INVALID_REQUEST;
390390+391391+ if (c->private != NULL)
392392+ {
393393+ /* a client may only have one service */
394394+ info = (svc_info_t *)c->private;
395395+ if (info->type != SERVICE_TYPE_TIMER_PRIVATE) return NOTIFY_STATUS_INVALID_REQUEST;
396396+397397+ /* the client must be asking for the same timer that is active */
398398+ timer = (timer_t *)info->private;
399399+ if ((timer->type != t) || (timer->start != s) || (timer->freq != f) || (timer->end != e) || (timer->day != d)) return NOTIFY_STATUS_INVALID_REQUEST;
400400+401401+ /* the client is already getting notifications for this timer */
402402+ return NOTIFY_STATUS_OK;
403403+ }
404404+405405+ switch (t)
406406+ {
407407+ case TIME_EVENT_ONESHOT:
408408+ {
409409+ timer = timer_oneshot(s, dispatch_get_main_queue());
410410+ break;
411411+ }
412412+ case TIME_EVENT_CLOCK:
413413+ {
414414+ timer = timer_clock(s, f, e, dispatch_get_main_queue());
415415+ break;
416416+ }
417417+ case TIME_EVENT_CAL:
418418+ {
419419+ timer = timer_calendar(s, f, d, e, dispatch_get_main_queue());
420420+ break;
421421+ }
422422+ default:
423423+ {
424424+ return NOTIFY_STATUS_FAILED;
425425+ }
426426+ }
427427+428428+ if (timer == NULL) return NOTIFY_STATUS_FAILED;
429429+ timer->context64 = c->client_id;
430430+431431+ info = (svc_info_t *)calloc(1, sizeof(svc_info_t));
432432+ assert(info != NULL);
433433+434434+ info->type = SERVICE_TYPE_TIMER_PRIVATE;
435435+ info->private = timer;
436436+ c->private = info;
437437+438438+ dispatch_source_set_event_handler(timer->src, ^{
439439+ dispatch_async(global.work_q, ^{
440440+ if (0 == dispatch_source_testcancel(timer->src))
441441+ {
442442+ daemon_post_client(timer->context64);
443443+ }
444444+ });
445445+ });
446446+447447+ dispatch_resume(timer->src);
448448+449449+ return NOTIFY_STATUS_OK;
450450+}
451451+452452+/* called from server-side routines in notify_proc - services are private to the client */
453453+int
454454+service_open(const char *name, client_t *client, uint32_t uid, uint32_t gid)
455455+{
456456+ uint32_t t, flags;
457457+ char *p, *q;
458458+459459+ t = service_type(name);
460460+461461+ switch (t)
462462+ {
463463+ case SERVICE_TYPE_NONE:
464464+ {
465465+ return NOTIFY_STATUS_OK;
466466+ }
467467+ case SERVICE_TYPE_PATH_PRIVATE:
468468+ {
469469+ p = strchr(name, ':');
470470+ if (p != NULL) p++;
471471+472472+ flags = 0;
473473+474474+ q = strchr(p, ':');
475475+ if (q != NULL)
476476+ {
477477+ flags = strtol(p, NULL, 0);
478478+ p = q + 1;
479479+ }
480480+481481+ return service_open_path_private(name, client, p, uid, gid, flags);
482482+ }
483483+ case SERVICE_TYPE_TIMER_PRIVATE:
484484+ {
485485+ p = strchr(name, ':');
486486+ if (p != NULL) p++;
487487+ return service_open_timer_private(name, client, p);
488488+ }
489489+ default:
490490+ {
491491+ return NOTIFY_STATUS_INVALID_REQUEST;
492492+ }
493493+ }
494494+495495+ return NOTIFY_STATUS_INVALID_REQUEST;
496496+}
497497+498498+void
499499+service_close(svc_info_t *info)
500500+{
501501+ if (info == NULL) return;
502502+503503+ switch (info->type)
504504+ {
505505+ case SERVICE_TYPE_PATH_PUBLIC:
506506+ case SERVICE_TYPE_PATH_PRIVATE:
507507+ {
508508+ path_node_close((path_node_t *)info->private);
509509+ break;
510510+ }
511511+ case SERVICE_TYPE_TIMER_PUBLIC:
512512+ case SERVICE_TYPE_TIMER_PRIVATE:
513513+ {
514514+ timer_close((timer_t *)info->private);
515515+ break;
516516+ }
517517+ default:
518518+ {
519519+ }
520520+ }
521521+522522+ free(info);
523523+}
+49
libnotify/notifyd/service.h
···11+/*
22+ * Copyright (c) 2003-2010 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#ifndef _NOTIFY_SERVICE_H_
2525+#define _NOTIFY_SERVICE_H_
2626+2727+#define SERVICE_TYPE_NONE 0
2828+#define SERVICE_TYPE_PATH_PUBLIC 1
2929+#define SERVICE_TYPE_PATH_PRIVATE 2
3030+#define SERVICE_TYPE_TIMER_PUBLIC 3
3131+#define SERVICE_TYPE_TIMER_PRIVATE 4
3232+3333+#define SERVICE_PREFIX "com.apple.system.notify.service."
3434+#define SERVICE_PREFIX_LEN 32
3535+3636+typedef struct
3737+{
3838+ uint32_t type;
3939+ void *private;
4040+} svc_info_t;
4141+4242+int service_open(const char *name, client_t *client, uint32_t uid, uint32_t gid);
4343+int service_open_path(const char *name, const char *path, uid_t uid, gid_t gid);
4444+int service_open_path_private(const char *name, client_t *client, const char *path, uid_t uid, gid_t gid, uint32_t flags);
4545+int service_open_timer(const char *name, const char *args);
4646+int service_open_timer_private(const char *name, client_t *client, const char *args);
4747+void service_close(svc_info_t *info);
4848+4949+#endif /* _NOTIFY_SERVICE_H_ */
+86
libnotify/notifyd/table.h
···11+/*
22+ * Copyright (c) 2003-2011 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#ifndef _NOTIFY_TABLE_H_
2525+#define _NOTIFY_TABLE_H_
2626+2727+#include <stdint.h>
2828+2929+typedef struct __table_private table_t;
3030+typedef struct __list_private list_t;
3131+3232+extern table_t *_nc_table_new(uint32_t n);
3333+3434+extern void _nc_table_insert(table_t *t, const char *key, void *datum);
3535+extern void _nc_table_insert_no_copy(table_t *t, const char *key, void *datum);
3636+extern void _nc_table_insert_n(table_t *t, uint32_t key, void *datum);
3737+extern void _nc_table_insert_64(table_t *t, uint64_t key, void *datum);
3838+3939+extern void *_nc_table_find(table_t *t, const char *key);
4040+extern void *_nc_table_find_n(table_t *t, uint32_t key);
4141+extern void *_nc_table_find_64(table_t *t, uint64_t key);
4242+4343+extern void _nc_table_delete(table_t *t, const char *key);
4444+extern void _nc_table_delete_n(table_t *t, uint32_t key);
4545+extern void _nc_table_delete_64(table_t *t, uint64_t key);
4646+4747+extern void *_nc_table_traverse_start(table_t *tin);
4848+extern void *_nc_table_traverse(table_t *tin, void *ttin);
4949+extern void _nc_table_traverse_end(table_t *tin, void *ttin);
5050+5151+extern void _nc_table_free(table_t *tin);
5252+5353+extern list_t *_nc_list_new(void *d);
5454+5555+extern list_t *_nc_list_retain(list_t *l);
5656+extern list_t *_nc_list_retain_list(list_t *l);
5757+5858+extern void _nc_list_release(list_t *l);
5959+extern void _nc_list_release_list(list_t *l);
6060+6161+extern list_t *_nc_list_prev(list_t *l);
6262+extern list_t *_nc_list_next(list_t *l);
6363+6464+extern void _nc_list_set_next(list_t *l, list_t *n);
6565+extern void _nc_list_set_prev(list_t *l, list_t *p);
6666+6767+extern list_t *_nc_list_head(list_t *l);
6868+extern list_t *_nc_list_tail(list_t *l);
6969+7070+extern list_t *_nc_list_prepend(list_t *l, list_t *n);
7171+extern list_t *_nc_list_append(list_t *l, list_t *n);
7272+7373+extern list_t *_nc_list_concat(list_t *a, list_t *b);
7474+7575+extern void *_nc_list_data(list_t *l);
7676+extern void _nc_list_set_data(list_t *l, void *d);
7777+7878+extern list_t *_nc_list_find(list_t *l, void *d);
7979+extern list_t *_nc_list_find_release(list_t *l, void *d);
8080+8181+extern list_t * _nc_list_reverse(list_t *l);
8282+extern uint32_t _nc_list_count(list_t *l);
8383+extern list_t *_nc_list_extract(list_t *n);
8484+extern list_t *_nc_list_chop(list_t *l);
8585+8686+#endif /* _NOTIFY_TABLE_H_ */
+465
libnotify/notifyd/timer.c
···11+/*
22+ * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#include <stdlib.h>
2525+#include <string.h>
2626+#include <Block.h>
2727+#include "timer.h"
2828+2929+#define MINUTE 60
3030+#define HOUR 3600
3131+#define DAY 86400
3232+3333+static const uint8_t mlen[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
3434+3535+/*
3636+ * Timed events
3737+ *
3838+ * Supported event types:
3939+ *
4040+ * Oneshot
4141+ * Every n seconds/minutes/hours/days/weeks
4242+ * Specific day of the month, every n months
4343+ * Specific weekday following specific day of the month, every n months
4444+ *
4545+ */
4646+static time_t
4747+timer_next(timer_t *t, time_t now)
4848+{
4949+ uint32_t y, m;
5050+ int32_t d, x, a, b, dd, wd;
5151+ struct tm tmp;
5252+ time_t next, tt, tod;
5353+5454+ if (t == NULL) return 0;
5555+5656+ switch (t->type)
5757+ {
5858+ case TIME_EVENT_ONESHOT:
5959+ {
6060+ /*
6161+ * oneshot time event
6262+ */
6363+ if (t->start < now) return 0;
6464+ return t->start;
6565+ }
6666+ case TIME_EVENT_CLOCK:
6767+ {
6868+ /*
6969+ * event recurs every t->freq seconds
7070+ */
7171+7272+ /* t->end is the cut-off. If it's in the past, return 0 */
7373+ if ((t->end != 0) && (t->end < now)) return 0;
7474+7575+ /* If the start time is in the future, that's the next occurrence */
7676+ if (t->start >= now) return t->start;
7777+7878+ /* shouldn't happen, as TIME_EVENT_CLOCK should always recur */
7979+ if (t->freq == 0) return 0;
8080+8181+ x = ((t->freq - 1) + now - t->start) / t->freq;
8282+ next = t->start + (x * t->freq);
8383+ return next;
8484+ }
8585+ case TIME_EVENT_CAL:
8686+ {
8787+ /*
8888+ * event recurs every t->freq months
8989+ * t->base gives us the starting month and year, and the time of day
9090+ * t->day specifies the day of the month (negative means relative to last day of the month)
9191+ * t->day is > 100 or < 100, then it means a weekday
9292+ * 101 = first monday, 102 = first tuesday, ..., 108 = second monday, and so on
9393+ * -101 = last monday, -102 = last tuesday, ..., -108 = second last monday, and so on
9494+ */
9595+9696+ /* t->end is the cut-off. If it's in the past, return 0 */
9797+ if ((t->end != 0) && (t->end < now)) return 0;
9898+9999+ /* If the start time is in the future, that's the next occurrence */
100100+ if (t->start >= now) return t->start;
101101+102102+ next = t->start;
103103+104104+ /* If t->next is set from the last time we ran, and it is in the past, we can start there. */
105105+ if ((t->next > 0) && (t->next < now)) next = t->next;
106106+107107+ while (next < now)
108108+ {
109109+ /* determine year, month, and time-of-day (clock time) of the next occurance */
110110+ memset(&tmp, 0, sizeof(struct tm));
111111+ localtime_r((const time_t *)&(next), &tmp);
112112+ y = tmp.tm_year;
113113+ m = tmp.tm_mon;
114114+ tod = tmp.tm_sec + (MINUTE * tmp.tm_min) + (HOUR * tmp.tm_hour);
115115+116116+ m += t->freq;
117117+ if (m > 11)
118118+ {
119119+ y += (m / 12);
120120+ m %= 12;
121121+ }
122122+123123+ /* we now have a year (y), a month (m), and a time of day (tod) */
124124+125125+ if (t->day > 0)
126126+ {
127127+ if (t->day < 100)
128128+ {
129129+ /* easy case: day is the date of the month */
130130+131131+ memset(&tmp, 0, sizeof(struct tm));
132132+ tmp.tm_year = y;
133133+ tmp.tm_mon = m;
134134+ tmp.tm_mday = t->day;
135135+ tmp.tm_isdst = -1;
136136+ next = mktime(&tmp) + tod;
137137+ continue;
138138+ }
139139+ else
140140+ {
141141+ /* t->day is a weekday */
142142+143143+ wd = t->day - 100;
144144+145145+ /* start by finding out the weekday of the first of the month */
146146+ memset(&tmp, 0, sizeof(struct tm));
147147+ tmp.tm_year = y;
148148+ tmp.tm_mon = m;
149149+ tmp.tm_mday = 1;
150150+ tmp.tm_isdst = -1;
151151+ tt = mktime(&tmp);
152152+ localtime_r((const time_t *)&tt, &tmp);
153153+154154+ if (tmp.tm_wday == 0) tmp.tm_wday = 7;
155155+156156+ x = 0;
157157+ if (tmp.tm_wday > (wd % 7)) x = (wd + 7) - tmp.tm_wday;
158158+ else x = wd - tmp.tm_wday;
159159+160160+ tmp.tm_mday += x;
161161+ tmp.tm_isdst = -1;
162162+ next = mktime(&tmp) + tod;
163163+ continue;
164164+ }
165165+ }
166166+167167+ if (t->day > -100)
168168+ {
169169+ /* nth day from the end of the month */
170170+ if (m == 1)
171171+ {
172172+ /* determine weekday of last day of February (== March 0) */
173173+ memset(&tmp, 0, sizeof(struct tm));
174174+ tmp.tm_year = y;
175175+ tmp.tm_mon = 2;
176176+ tmp.tm_mday = 0;
177177+ tmp.tm_isdst = -1;
178178+ tt = mktime(&tmp);
179179+ memset(&tmp, 0, sizeof(struct tm));
180180+ localtime_r((const time_t *)&(tt), &tmp);
181181+ d = tmp.tm_mday + t->day;
182182+ }
183183+ else
184184+ {
185185+ d = mlen[m] + t->day;
186186+ }
187187+188188+ memset(&tmp, 0, sizeof(struct tm));
189189+ tmp.tm_year = y;
190190+ tmp.tm_mon = m;
191191+ tmp.tm_mday = d;
192192+ tmp.tm_isdst = -1;
193193+ next = mktime(&tmp) + tod;
194194+ continue;
195195+ }
196196+197197+ /* t->day is a weekday relative to the end of the month */
198198+ if (m == 1)
199199+ {
200200+ /* determine weekday of last day of February (== March 0) */
201201+ memset(&tmp, 0, sizeof(struct tm));
202202+ tmp.tm_year = y;
203203+ tmp.tm_mon = 2;
204204+ tmp.tm_mday = 0;
205205+ tmp.tm_isdst = -1;
206206+ tt = mktime(&tmp);
207207+ memset(&tmp, 0, sizeof(struct tm));
208208+ localtime_r((const time_t *)&(tt), &tmp);
209209+ d = tmp.tm_mday;
210210+ }
211211+ else
212212+ {
213213+ d = mlen[m];
214214+ }
215215+216216+ memset(&tmp, 0, sizeof(struct tm));
217217+ tmp.tm_year = y;
218218+ tmp.tm_mon = m;
219219+ tmp.tm_mday = d;
220220+ tmp.tm_isdst = -1;
221221+222222+ dd = -1 * (t->day + 100);
223223+ a = dd % 7;
224224+ b = (dd + 6) / 7;
225225+ if (a <= tmp.tm_wday) b--;
226226+ tmp.tm_mday = ((a - tmp.tm_wday) + d) - (b * 7);
227227+ next = mktime(&tmp) + tod;
228228+ }
229229+230230+ t->next = next;
231231+ return next;
232232+ }
233233+ default:
234234+ {
235235+ return 0;
236236+ }
237237+ }
238238+239239+ return 0;
240240+}
241241+242242+/*
243243+ * This does the actual free.
244244+ * It is dispatched on the timer's dispatch source queue to make it safe.
245245+ */
246246+static void
247247+timer_free(timer_t *t)
248248+{
249249+ if (t == NULL) return;
250250+ if (t->deactivation_handler != NULL) Block_release(t->deactivation_handler);
251251+ if (t->contextp != NULL) free(t->contextp);
252252+253253+ dispatch_release(t->t_src);
254254+ dispatch_release(t->t_queue);
255255+256256+ memset(t, 0, sizeof(timer_t));
257257+ free(t);
258258+}
259259+260260+void
261261+timer_close(timer_t *t)
262262+{
263263+ if (t == NULL) return;
264264+265265+ if (t->t_src != NULL) dispatch_source_cancel(t->t_src);
266266+267267+ /*
268268+ * We need to make sure that the source's event handler isn't currently running
269269+ * before we free the timer. We let the source's queue do the actual free.
270270+ */
271271+ dispatch_async(t->t_queue, ^{ timer_free(t); });
272272+}
273273+274274+timer_t *
275275+timer_oneshot(time_t when, dispatch_queue_t queue)
276276+{
277277+ timer_t *t;
278278+ time_t now;
279279+ dispatch_time_t trigger;
280280+281281+ /* refuse a trigger time in the past */
282282+ now = time(0);
283283+ if (when <= now) return NULL;
284284+285285+ t = calloc(1, sizeof(timer_t));
286286+ if (t == NULL) return NULL;
287287+288288+ dispatch_retain(queue);
289289+290290+ t->type = TIME_EVENT_ONESHOT;
291291+ t->start = when;
292292+ t->t_queue = queue;
293293+294294+ t->t_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
295295+ t->src = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
296296+297297+ trigger = dispatch_walltime(NULL, (t->start - now) * NSEC_PER_SEC);
298298+ dispatch_source_set_timer(t->t_src, trigger, NSEC_PER_SEC, 0);
299299+300300+ dispatch_source_set_event_handler(t->t_src, ^{
301301+ dispatch_source_merge_data(t->src, 1);
302302+ dispatch_source_cancel(t->t_src);
303303+ if (t->deactivation_handler != NULL)
304304+ {
305305+ dispatch_async(t->t_queue, ^{ t->deactivation_handler(); });
306306+ }
307307+ });
308308+309309+ dispatch_resume(t->t_src);
310310+ return t;
311311+}
312312+313313+void
314314+timer_set_deactivation_handler(timer_t *t, void(^handler)())
315315+{
316316+ if (t == NULL) return;
317317+318318+ if (t->deactivation_handler != NULL) Block_release(t->deactivation_handler);
319319+ t->deactivation_handler = Block_copy(handler);
320320+}
321321+322322+timer_t *
323323+timer_clock(time_t first, time_t freq_sec, time_t end, dispatch_queue_t queue)
324324+{
325325+ timer_t *t;
326326+ time_t now;
327327+ dispatch_time_t trigger;
328328+ int64_t x;
329329+330330+ if (freq_sec == 0) return timer_oneshot(first, queue);
331331+332332+ now = time(0);
333333+334334+ t = calloc(1, sizeof(timer_t));
335335+ if (t == NULL) return NULL;
336336+337337+ t->type = TIME_EVENT_CLOCK;
338338+339339+ if (first < now)
340340+ {
341341+ x = ((freq_sec - 1) + now - first) / freq_sec;
342342+ t->start = first + (x * freq_sec);
343343+ }
344344+ else
345345+ {
346346+ t->start = first;
347347+ }
348348+349349+ t->end = end;
350350+ t->freq = freq_sec;
351351+ t->t_queue = queue;
352352+353353+ t->t_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
354354+ t->src = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
355355+356356+ trigger = dispatch_walltime(NULL, (t->start - now) * NSEC_PER_SEC);
357357+ dispatch_source_set_timer(t->t_src, trigger, freq_sec * NSEC_PER_SEC, 0);
358358+359359+ dispatch_source_set_event_handler(t->t_src, ^{
360360+ unsigned long n = dispatch_source_get_data(t->t_src);
361361+ dispatch_source_merge_data(t->src, n);
362362+363363+ /* deactivate if this is the last time we want to trigger the client source */
364364+ if ((t->end > 0) && (t->end < (time(0) + freq_sec)))
365365+ {
366366+ dispatch_source_cancel(t->t_src);
367367+ if (t->deactivation_handler != NULL)
368368+ {
369369+ dispatch_async(t->t_queue, ^{ t->deactivation_handler(); });
370370+ }
371371+ }
372372+ });
373373+374374+ dispatch_resume(t->t_src);
375375+376376+ return t;
377377+}
378378+379379+timer_t *
380380+timer_calendar(time_t first, time_t freq_mth, time_t end, int day, dispatch_queue_t queue)
381381+{
382382+ timer_t *t;
383383+ time_t next, now;
384384+ dispatch_time_t trigger;
385385+386386+ if (freq_mth == 0) return timer_oneshot(first, queue);
387387+388388+ now = time(0);
389389+390390+ t = calloc(1, sizeof(timer_t));
391391+ if (t == NULL) return NULL;
392392+393393+ t->type = TIME_EVENT_CAL;
394394+ t->start = first;
395395+ t->day = day;
396396+ t->end = end;
397397+ t->freq = freq_mth;
398398+ t->t_queue = queue;
399399+400400+ t->t_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
401401+ t->src = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
402402+403403+ next = timer_next(t, now);
404404+ trigger = dispatch_walltime(NULL, (next - now) * NSEC_PER_SEC);
405405+ dispatch_source_set_timer(t->t_src, trigger, NSEC_PER_SEC, 0);
406406+407407+ dispatch_source_set_event_handler(t->t_src, ^{
408408+ unsigned long n = dispatch_source_get_data(t->t_src);
409409+ dispatch_source_merge_data(t->src, n);
410410+411411+ time_t now = time(0);
412412+ time_t x = timer_next(t, now);
413413+414414+ /* deactivate when there is no next time */
415415+ if (x == 0)
416416+ {
417417+ dispatch_source_cancel(t->t_src);
418418+ if (t->deactivation_handler != NULL)
419419+ {
420420+ dispatch_async(t->t_queue, ^{ t->deactivation_handler(); });
421421+ }
422422+ }
423423+ else
424424+ {
425425+ dispatch_source_set_timer(t->t_src, dispatch_walltime(NULL, (x - now) * NSEC_PER_SEC), NSEC_PER_SEC, 0);
426426+ }
427427+ });
428428+429429+ dispatch_resume(t->t_src);
430430+431431+ return t;
432432+}
433433+434434+timer_t *
435435+timer_calendar_long(uint32_t start_year, uint32_t start_month, uint32_t start_day, uint32_t start_hour, uint32_t start_min, uint32_t start_sec, time_t freq, int day, uint32_t end_year, uint32_t end_month, uint32_t end_day, uint32_t end_hour, uint32_t end_min, uint32_t end_sec, dispatch_queue_t queue)
436436+{
437437+ struct tm tmp;
438438+ time_t first, last;
439439+440440+ memset(&tmp, 0, sizeof(struct tm));
441441+ tmp.tm_year = start_year - 1900;
442442+ tmp.tm_mon = start_month;
443443+ tmp.tm_mday = start_day;
444444+ tmp.tm_isdst = -1;
445445+ tmp.tm_hour = start_hour;
446446+ tmp.tm_min = start_min;
447447+ tmp.tm_sec = start_sec;
448448+449449+ first = mktime(&tmp);
450450+451451+ if (freq == 0) return timer_oneshot(first, queue);
452452+453453+ memset(&tmp, 0, sizeof(struct tm));
454454+ tmp.tm_year = end_year;
455455+ tmp.tm_mon = end_month;
456456+ tmp.tm_mday = end_day;
457457+ tmp.tm_isdst = -1;
458458+ tmp.tm_hour = end_hour;
459459+ tmp.tm_min = end_min;
460460+ tmp.tm_sec = end_sec;
461461+462462+ last = mktime(&tmp);
463463+464464+ return timer_calendar(first, freq, day, last, queue);
465465+}
+59
libnotify/notifyd/timer.h
···11+/*
22+ * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#include <time.h>
2525+#include <stdint.h>
2626+#include <dispatch/dispatch.h>
2727+2828+#define TIME_EVENT_NONE 0
2929+#define TIME_EVENT_ONESHOT 1
3030+#define TIME_EVENT_CLOCK 2
3131+#define TIME_EVENT_CAL 3
3232+3333+/*
3434+ * Timer Event
3535+ */
3636+typedef struct
3737+{
3838+ uint32_t type;
3939+ int64_t start;
4040+ int64_t end;
4141+ uint32_t freq;
4242+ int32_t day;
4343+ int64_t next;
4444+ void (^deactivation_handler)();
4545+ dispatch_source_t src;
4646+ dispatch_source_t t_src;
4747+ dispatch_queue_t t_queue;
4848+ void *contextp;
4949+ uint32_t context32;
5050+ uint64_t context64;
5151+} timer_t;
5252+5353+timer_t *timer_oneshot(time_t when, dispatch_queue_t queue);
5454+timer_t *timer_clock(time_t first, time_t freq_sec, time_t end, dispatch_queue_t queue);
5555+timer_t *timer_calendar(time_t first, time_t freq_mth, time_t end, int day, dispatch_queue_t queue);
5656+timer_t *timer_calendar_long(uint32_t yf, uint32_t mf, uint32_t df, uint32_t hf, uint32_t nf, uint32_t sf, time_t fm, int d, uint32_t ye, uint32_t me, uint32_t de, uint32_t he, uint32_t ne, uint32_t se, dispatch_queue_t queue);
5757+5858+void timer_set_deactivation_handler(timer_t *t, void(^handler)());
5959+void timer_close(timer_t *t);
···11+.\" Copyright (c) 2006-2011 Apple Inc. All rights reserved.
22+.\"
33+.\" @APPLE_LICENSE_HEADER_START@
44+.\"
55+.\" This file contains Original Code and/or Modifications of Original Code
66+.\" as defined in and that are subject to the Apple Public Source License
77+.\" Version 2.0 (the 'License'). You may not use this file except in
88+.\" compliance with the License. Please obtain a copy of the License at
99+.\" http://www.opensource.apple.com/apsl/ and read it before using this
1010+.\" file.
1111+.\"
1212+.\" The Original Code and all software distributed under the License are
1313+.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1414+.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1515+.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1616+.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1717+.\" Please see the License for the specific language governing rights and
1818+.\" limitations under the License.
1919+.\"
2020+.\" @APPLE_LICENSE_HEADER_END@
2121+.\"
2222+.\"
2323+.Dd November 4, 2011
2424+.Dt notifyutil 1
2525+.Os "Mac OS X"
2626+.Sh NAME
2727+.Nm notifyutil
2828+.Nd notification command line utility
2929+.Sh SYNOPSIS
3030+.Nm
3131+.Op Fl q
3232+.Op Fl v
3333+.Op Fl z Ar msec
3434+.Op Fl M
3535+.Op Fl R
3636+.Op command Li ...
3737+.Pp
3838+.Sh DESCRIPTION
3939+.Nm
4040+is a command-line utility for interacting with the
4141+.Xr notify 3
4242+notification system and the
4343+.Xr notifyd 8
4444+server.
4545+It may be used to post notifications, detect and report notifications,
4646+and to examine and set the state values associated with notification keys.
4747+.Pp
4848+If
4949+.Nm
5050+is used to monitor one or more notification keys,
5151+it prints the notification key when the corresponding notification is received.
5252+The
5353+.Fl v
5454+(verbose)
5555+and
5656+.Fl q
5757+(quiet) flags, if specified, modify the output behavior.
5858+.Pp
5959+The
6060+.Fl v
6161+flag causes
6262+.Nm
6363+to print a time stamp, the notification key, the current state value for that key,
6464+and the type of the notification (port, file, etc).
6565+The
6666+.Fl q
6767+flag supresses any output except for state values fetched following a
6868+.Fl g
6969+command.
7070+.Pp
7171+Commands listed in the table below are processed in left to right order from the command line.
7272+.Pp
7373+.Bl -tag -width "-signal [#]" -compact -offset indent
7474+.It Fl p Ar key
7575+Post a notification for
7676+.Ar key .
7777+.It Fl w Ar key
7878+Register for
7979+.Ar key
8080+and wait forever for notifications.
8181+.It Fl Ar # Ar key
8282+Register for
8383+.Ar key
8484+and wait for
8585+.Ar #
8686+(an integer) notifications.
8787+.It ""
8888+.Li E.g.
8989+.Fl 1 Ar key
9090+waits for a single notification.
9191+.It Fl g Ar key
9292+Get state value for
9393+.Ar key .
9494+.It Fl s Ar key Ar val
9595+Set state value for
9696+.Ar key .
9797+.It Fl port
9898+Use mach port notifications for subsequent
9999+.Fl w
100100+or
101101+.Fl Ar #
102102+registrations.
103103+.It ""
104104+This is the default registration type.
105105+.It Fl file
106106+Use file descriptor notifications for subsequent registrations.
107107+.It Fl check
108108+Use shared memory notifications for subsequent registrations.
109109+.It Fl signal Op Ar #
110110+Use signal notifications for subsequent registrations.
111111+.It ""
112112+Signal 1 (HUP) is the default, but an alternate signal may be specified.
113113+.It Fl dispatch
114114+Use dispatch for subsequent registrations.
115115+.El
116116+.Pp
117117+When invoked with any combination of
118118+.Fl w
119119+and
120120+.Fl Ar #
121121+actions,
122122+.Nm
123123+registers for notification for the specified key(s).
124124+If any key is given with a
125125+.Fl w
126126+action,
127127+.Nm
128128+runs until interrupted with Control-C.
129129+If all registrations are invoked with
130130+.Fl Ar # ,
131131+the program continues to run until the corresponding number of notifications for each key have been received.
132132+.Pp
133133+By default,
134134+.Nm
135135+uses mach port registration (using
136136+.Fn notify_register_mach_port )
137137+for keys given with a
138138+.Fl w
139139+or
140140+.Fl Ar #
141141+flag.
142142+The
143143+.Fl file
144144+command causes
145145+.Nm
146146+to use
147147+.Fn notify_register_file_descriptor
148148+for any subsequent
149149+.Fl w
150150+or
151151+.Fl Ar #
152152+registrations.
153153+Similarly,
154154+.Fl check
155155+causes
156156+.Nm
157157+to use
158158+.Fn notify_register_check
159159+for subsequent registrations,
160160+.Fl signal
161161+switches to
162162+.Fn notify_register_signal ,
163163+and
164164+.Fl dispatch
165165+causes it to use
166166+.Fn notify_register_dispatch
167167+for subsequent registrations.
168168+.Pp
169169+If any registrations are made following the use of the
170170+.Fl check
171171+command,
172172+.Nm
173173+will start a timer and check for shared memory notifications every 100 milliseconds.
174174+An alternate timer value may be set following the
175175+.Fl z
176176+flag.
177177+.Pp
178178+The
179179+.Fl M
180180+flag causes
181181+.Nm
182182+to use multiplex all notifications over a single mach connection with
183183+.Nm notifyd .
184184+Notifications (except shared memory notifications)
185185+are received and redistributed by a dispatch handler.
186186+.Pp
187187+The
188188+.Fl R
189189+flag causes
190190+.Nm notifyutil
191191+to regenerate all its registrations in the unlikely event that
192192+.Nm notifyd
193193+restarts.
194194+.Pp
195195+Note that a notification key and its associated state variable only exist
196196+when there are one or more current registrations for that key.
197197+Setting the state for a key that has no registrations has no effect.
198198+Thus the command
199199+.Pp
200200+.Dl notifyutil -s foo.bar 123 -g foo.bar
201201+.Pp
202202+will print
203203+.Pp
204204+.Dl foo.bar 0
205205+.Pp
206206+unless foo.bar is registered by some other process.
207207+However, the command
208208+.Pp
209209+.Dl notifyutil -w foo.bar -s foo.bar 123 -g foo.bar
210210+.Pp
211211+prints
212212+.Pp
213213+.Dl foo.bar 123
214214+.Pp
215215+since the
216216+.Dq -w foo.bar
217217+registration ensures the key and its state variable exist before the value is set,
218218+and continue to exist when the value is fetched.
219219+.Sh SEE ALSO
220220+notify(3), notifyd(8)
+751
libnotify/notifyutil/notifyutil.c
···11+/*
22+ * Copyright (c) 2006-2010 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#include <stdio.h>
2525+#include <stdint.h>
2626+#include <stdlib.h>
2727+#include <unistd.h>
2828+#include <string.h>
2929+#include <time.h>
3030+#include <sys/time.h>
3131+#include <mach/mach.h>
3232+#include <notify.h>
3333+#include <notify_private.h>
3434+#include <signal.h>
3535+#include <dispatch/dispatch.h>
3636+3737+#define forever for(;;)
3838+#define IndexNull ((uint32_t)-1)
3939+4040+#define PRINT_QUIET 0x00000000
4141+#define PRINT_KEY 0x00000001
4242+#define PRINT_STATE 0x00000002
4343+#define PRINT_TIME 0x00000004
4444+#define PRINT_TYPE 0x00000008
4545+#define PRINT_VERBOSE 0xffffffff
4646+4747+#ifndef USEC_PER_SEC
4848+#define USEC_PER_SEC 1000000
4949+#endif
5050+5151+#define TYPE_NULL 0
5252+#define TYPE_PORT 1
5353+#define TYPE_FILE 2
5454+#define TYPE_DISPATCH 3
5555+#define TYPE_SIGNAL 4
5656+#define TYPE_CHECK 5
5757+#define TYPE_PLAIN 6
5858+5959+static const char *typename[] =
6060+{
6161+ "unknown",
6262+ "port",
6363+ "file",
6464+ "dispatch",
6565+ "signal",
6666+ "check",
6767+ "plain"
6868+};
6969+7070+extern uint32_t notify_register_plain(const char *name, int *out_token);
7171+7272+typedef struct
7373+{
7474+ uint32_t token;
7575+ uint32_t type;
7676+ uint32_t signum;
7777+ uint32_t count;
7878+ char *name;
7979+} reg_entry_t;
8080+8181+static reg_entry_t *reg;
8282+static uint32_t reg_count = 0;
8383+8484+static int printopt;
8585+static int port_flag;
8686+static int file_flag;
8787+static int watch_file;
8888+static mach_port_t watch_port;
8989+dispatch_source_t timer_src;
9090+dispatch_source_t port_src;
9191+dispatch_source_t file_src;
9292+dispatch_source_t sig_src[__DARWIN_NSIG];
9393+dispatch_queue_t watch_queue;
9494+9595+static void
9696+usage(const char *name)
9797+{
9898+ fprintf(stderr, "usage: %s [-q] [-v] [-z msec] [-M] [-R] [command ...]\n", name);
9999+ fprintf(stderr, " -q quiet mode\n");
100100+ fprintf(stderr, " -v verbose - prints time, key, state value, and type\n");
101101+ fprintf(stderr, " -z msec pause msec milliseconds after posting [default 100]\n");
102102+ fprintf(stderr, " -M multiplex notifications from notifyd over a single mach port\n");
103103+ fprintf(stderr, " -R regenerate registrations if notifyd restarts\n");
104104+ fprintf(stderr, "commands:\n");
105105+ fprintf(stderr, " -port switch to mach port for subsequent registrations [default]\n");
106106+ fprintf(stderr, " -file switch to file descriptor for subsequent registrations\n");
107107+ fprintf(stderr, " -check switch to shared memory for subsequent registrations\n");
108108+ fprintf(stderr, " -signal [#] switch to signal [#] for subsequent registrations\n");
109109+ fprintf(stderr, " initial default for signal is 1 (SIGHUP)\n");
110110+ fprintf(stderr, " -dispatch switch to dispatch for subsequent registrations\n");
111111+ fprintf(stderr, " -p key post a notifcation for key\n");
112112+ fprintf(stderr, " -w key register for key and report notifications\n");
113113+ fprintf(stderr, " -# key (# is an integer value, eg \"-1\") register for key and report # notifications\n");
114114+ fprintf(stderr, " -g key get state value for key\n");
115115+ fprintf(stderr, " -s key val set state value for key\n");
116116+}
117117+118118+static const char *
119119+notify_status_strerror(int status)
120120+{
121121+ switch (status)
122122+ {
123123+ case NOTIFY_STATUS_OK: return("OK");
124124+ case NOTIFY_STATUS_INVALID_NAME: return "Invalid Name";
125125+ case NOTIFY_STATUS_INVALID_TOKEN: return "Invalid Token";
126126+ case NOTIFY_STATUS_INVALID_PORT: return "Invalid Port";
127127+ case NOTIFY_STATUS_INVALID_FILE: return "Invalid File";
128128+ case NOTIFY_STATUS_INVALID_SIGNAL: return "Invalid Signal";
129129+ case NOTIFY_STATUS_INVALID_REQUEST: return "Invalid Request";
130130+ case NOTIFY_STATUS_NOT_AUTHORIZED: return "Not Authorized";
131131+ case NOTIFY_STATUS_FAILED:
132132+ default: return "Failed";
133133+ }
134134+}
135135+136136+static void
137137+reg_add(uint32_t tid, uint32_t type, uint32_t signum, uint32_t count, const char *name)
138138+{
139139+ if (name == NULL) return;
140140+141141+ reg = (reg_entry_t *)reallocf(reg, (reg_count + 1) * sizeof(reg_entry_t));
142142+ if (reg == NULL)
143143+ {
144144+ fprintf(stderr, "Can't allocate memory!\n");
145145+ reg_count = 0;
146146+ return;
147147+ }
148148+149149+ reg[reg_count].token = tid;
150150+ reg[reg_count].type = type;
151151+ reg[reg_count].signum = signum;
152152+ reg[reg_count].count = count;
153153+ reg[reg_count].name = strdup(name);
154154+ if (reg[reg_count].name == NULL)
155155+ {
156156+ fprintf(stderr, "Can't allocate memory!\n");
157157+ reg = NULL;
158158+ reg_count = 0;
159159+ return;
160160+ }
161161+162162+ reg_count++;
163163+}
164164+165165+static void
166166+reg_delete(uint32_t index)
167167+{
168168+ uint32_t i;
169169+170170+ if (index == IndexNull) return;
171171+ if (index >= reg_count) return;
172172+173173+ free(reg[index].name);
174174+175175+ for (i = index + 1; i < reg_count; i++) reg[i - 1] = reg[i];
176176+ reg_count--;
177177+178178+ if (reg_count == 0)
179179+ {
180180+ free(reg);
181181+ reg = NULL;
182182+ }
183183+ else
184184+ {
185185+ reg = (reg_entry_t *)reallocf(reg, reg_count * sizeof(reg_entry_t));
186186+ if (reg == NULL)
187187+ {
188188+ fprintf(stderr, "Can't allocate memory!\n");
189189+ reg_count = 0;
190190+ }
191191+ }
192192+}
193193+194194+static uint32_t
195195+reg_find_name(const char *name)
196196+{
197197+ uint32_t i;
198198+199199+ for (i = 0; i < reg_count; i++) if (!strcmp(reg[i].name, name)) return i;
200200+ return IndexNull;
201201+}
202202+203203+static uint32_t
204204+reg_find_token(uint32_t tid)
205205+{
206206+ uint32_t i;
207207+208208+ for (i = 0; i < reg_count; i++) if (tid == reg[i].token) return i;
209209+ return IndexNull;
210210+}
211211+212212+static void
213213+process_event(int tid)
214214+{
215215+ struct timeval now;
216216+ char tstr[32];
217217+ int status, index, needspace;
218218+ uint64_t state;
219219+220220+ gettimeofday(&now, NULL);
221221+222222+ index = reg_find_token(tid);
223223+ if (index == IndexNull) return;
224224+225225+ needspace = 0;
226226+227227+ if (printopt & PRINT_TIME)
228228+ {
229229+ snprintf(tstr, sizeof(tstr), "%llu", now.tv_usec + USEC_PER_SEC + 500);
230230+ tstr[4] = '\0';
231231+ printf("%d.%s", (int)now.tv_sec, tstr+1);
232232+ needspace = 1;
233233+ }
234234+235235+ if (printopt & PRINT_KEY)
236236+ {
237237+ if (needspace) printf(" ");
238238+ printf("%s", reg[index].name);
239239+ needspace = 1;
240240+ }
241241+242242+ if (printopt & PRINT_STATE)
243243+ {
244244+ if (needspace) printf(" ");
245245+ state = 0;
246246+ status = notify_get_state(tid, &state);
247247+ if (status == NOTIFY_STATUS_OK) printf("%llu",(unsigned long long)state);
248248+ else printf(": %s", notify_status_strerror(status));
249249+ needspace = 1;
250250+ }
251251+252252+ if (printopt & PRINT_TYPE)
253253+ {
254254+ if (needspace) printf(" ");
255255+ printf("%s", typename[reg[index].type]);
256256+ needspace = 1;
257257+ }
258258+259259+ if (printopt != PRINT_QUIET) printf("\n");
260260+261261+ if ((reg[index].count != IndexNull) && (reg[index].count != 0)) reg[index].count--;
262262+ if (reg[index].count == 0)
263263+ {
264264+ status = notify_cancel(tid);
265265+ reg_delete(index);
266266+ }
267267+}
268268+269269+static void
270270+file_handler(int fd)
271271+{
272272+ ssize_t i;
273273+ int tid;
274274+275275+ if (fd < 0) return;
276276+277277+ i = read(fd, &tid, sizeof(tid));
278278+ if (i < 0) return;
279279+280280+ tid = ntohl(tid);
281281+ process_event(tid);
282282+283283+ if (reg_count == 0) exit(0);
284284+}
285285+286286+static void
287287+port_handler(mach_port_t port)
288288+{
289289+ int tid;
290290+ mach_msg_empty_rcv_t msg;
291291+ kern_return_t status;
292292+293293+ if (port == MACH_PORT_NULL) return;
294294+295295+ memset(&msg, 0, sizeof(msg));
296296+ status = mach_msg(&msg.header, MACH_RCV_MSG, 0, sizeof(msg), port, 0, MACH_PORT_NULL);
297297+ if (status != KERN_SUCCESS) return;
298298+299299+ tid = msg.header.msgh_id;
300300+ process_event(tid);
301301+302302+ if (reg_count == 0) exit(0);
303303+}
304304+305305+static void
306306+signal_handler(uint32_t sig)
307307+{
308308+ uint32_t i, status;
309309+ int check;
310310+311311+ if (printopt != PRINT_QUIET) printf("SIGNAL %u\n", sig);
312312+ for (i = 0; i < reg_count; i++)
313313+ {
314314+ if ((reg[i].type == TYPE_SIGNAL) && (reg[i].signum == sig))
315315+ {
316316+ check = 0;
317317+ status = notify_check(reg[i].token, &check);
318318+ if ((status == NOTIFY_STATUS_OK) && (check != 0)) process_event(reg[i].token);
319319+ }
320320+ }
321321+322322+ if (reg_count == 0) exit(0);
323323+}
324324+325325+static void
326326+dispatch_handler(const char *name)
327327+{
328328+ uint32_t index = reg_find_name(name);
329329+ if (index == IndexNull) return;
330330+331331+ process_event(reg[index].token);
332332+}
333333+334334+static void
335335+timer_handler(void)
336336+{
337337+ uint32_t i, status;
338338+ int check;
339339+340340+ for (i = 0; i < reg_count; i++)
341341+ {
342342+ if ((reg[i].type == TYPE_CHECK) || (reg[i].type == TYPE_PLAIN))
343343+ {
344344+ check = 0;
345345+ status = notify_check(reg[i].token, &check);
346346+ if ((status == NOTIFY_STATUS_OK) && (check != 0)) process_event(reg[i].token);
347347+ }
348348+ }
349349+350350+ if (reg_count == 0) exit(0);
351351+}
352352+353353+static uint32_t
354354+do_register(const char *name, uint32_t type, uint32_t signum, uint32_t count)
355355+{
356356+ int tid, check;
357357+ uint32_t status;
358358+359359+ switch (type)
360360+ {
361361+ case TYPE_PORT:
362362+ {
363363+ status = notify_register_mach_port(name, &watch_port, port_flag, &tid);
364364+ if (status != NOTIFY_STATUS_OK) return status;
365365+366366+ port_flag = NOTIFY_REUSE;
367367+ if (port_src == NULL)
368368+ {
369369+ port_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, watch_port, 0, watch_queue);
370370+ dispatch_source_set_event_handler(port_src, ^{
371371+ port_handler(watch_port);
372372+ });
373373+ dispatch_resume(port_src);
374374+ }
375375+376376+ break;
377377+ }
378378+379379+ case TYPE_FILE:
380380+ {
381381+ status = notify_register_file_descriptor(name, &watch_file, file_flag, &tid);
382382+ if (status != NOTIFY_STATUS_OK) return status;
383383+384384+ file_flag = NOTIFY_REUSE;
385385+ if (file_src == NULL)
386386+ {
387387+ file_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)watch_file, 0, watch_queue);
388388+ dispatch_source_set_event_handler(file_src, ^{
389389+ file_handler(watch_file);
390390+ });
391391+ dispatch_resume(file_src);
392392+ }
393393+394394+ break;
395395+ }
396396+397397+ case TYPE_SIGNAL:
398398+ {
399399+ signal(signum, SIG_IGN);
400400+401401+ status = notify_register_signal(name, signum, &tid);
402402+ if (status != NOTIFY_STATUS_OK) return status;
403403+404404+ status = notify_check(tid, &check);
405405+406406+ if (sig_src[signum] == NULL)
407407+ {
408408+ sig_src[signum] = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)signum, 0, watch_queue);
409409+ dispatch_source_set_event_handler(sig_src[signum], ^{
410410+ signal_handler(signum);
411411+ });
412412+ dispatch_resume(sig_src[signum]);
413413+ }
414414+415415+ break;
416416+ }
417417+418418+ case TYPE_DISPATCH:
419419+ {
420420+ status = notify_register_dispatch(name, &tid, watch_queue, ^(int x){ dispatch_handler(name); });
421421+ if (status != NOTIFY_STATUS_OK) return status;
422422+ break;
423423+ }
424424+425425+ case TYPE_CHECK:
426426+ {
427427+ status = notify_register_check(name, &tid);
428428+ if (status != NOTIFY_STATUS_OK) return status;
429429+430430+ status = notify_check(tid, &check);
431431+432432+ if (timer_src == NULL)
433433+ {
434434+ timer_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, watch_queue);
435435+ dispatch_source_set_event_handler(timer_src, ^{
436436+ timer_handler();
437437+ });
438438+ dispatch_source_set_timer(timer_src, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 10), NSEC_PER_SEC / 10, 0);
439439+ dispatch_resume(timer_src);
440440+ }
441441+442442+ break;
443443+ }
444444+445445+ case TYPE_PLAIN:
446446+ {
447447+ status = notify_register_plain(name, &tid);
448448+ if (status != NOTIFY_STATUS_OK) return status;
449449+450450+ status = notify_check(tid, &check);
451451+452452+ if (timer_src == NULL)
453453+ {
454454+ timer_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, watch_queue);
455455+ dispatch_source_set_event_handler(timer_src, ^{
456456+ timer_handler();
457457+ });
458458+ dispatch_source_set_timer(timer_src, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 10), NSEC_PER_SEC / 10, 0);
459459+ dispatch_resume(timer_src);
460460+ }
461461+462462+ break;
463463+ }
464464+465465+ default: return NOTIFY_STATUS_FAILED;
466466+ }
467467+468468+ reg_add(tid, type, signum, count, name);
469469+ return NOTIFY_STATUS_OK;
470470+}
471471+472472+int
473473+main(int argc, const char *argv[])
474474+{
475475+ const char *name;
476476+ uint32_t i, n, index, signum, ntype, status, opts, nap;
477477+ int tid;
478478+ uint64_t state;
479479+480480+ for (i = 0; i < __DARWIN_NSIG; i++) sig_src[i] = NULL;
481481+482482+ ntype = TYPE_PORT;
483483+ signum = 1;
484484+ watch_file = -1;
485485+ watch_port = MACH_PORT_NULL;
486486+ printopt = PRINT_KEY;
487487+ opts = 0;
488488+ nap = 100000;
489489+490490+ watch_queue = dispatch_queue_create("Watch Q", NULL);
491491+492492+ name = strrchr(argv[0], '/');
493493+ if (name == NULL) name = argv[0];
494494+ else name++;
495495+496496+ for (i = 1; i < argc; i++)
497497+ {
498498+ if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "-h")))
499499+ {
500500+ usage(name);
501501+ exit(0);
502502+ }
503503+ else if (!strcmp(argv[i], "-q"))
504504+ {
505505+ printopt = PRINT_QUIET;
506506+ }
507507+ else if (!strcmp(argv[i], "-v"))
508508+ {
509509+ printopt |= PRINT_VERBOSE;
510510+ }
511511+ else if (!strcmp(argv[i], "-M"))
512512+ {
513513+ opts |= NOTIFY_OPT_DEMUX;
514514+ }
515515+ else if (!strcmp(argv[i], "-R"))
516516+ {
517517+ opts |= NOTIFY_OPT_REGEN;
518518+ }
519519+ else if (!strcmp(argv[i], "-z"))
520520+ {
521521+ if ((i + 1) >= argc)
522522+ {
523523+ fprintf(stderr, "timer value must be supplied following -z\n");
524524+ usage(name);
525525+ exit(1);
526526+ }
527527+528528+ i++;
529529+530530+ if ((argv[i][0] < '0') || (argv[i][1] > '9'))
531531+ {
532532+ fprintf(stderr, "-z %s is invalid\n", argv[i]);
533533+ fprintf(stderr, "timer value must be an integer\n");
534534+ usage(name);
535535+ exit(1);
536536+ }
537537+ nap = 1000 * atoi(argv[i]);
538538+ }
539539+ else if (!strcmp(argv[i], "-port"))
540540+ {}
541541+ else if (!strcmp(argv[i], "-file"))
542542+ {}
543543+ else if ((!strcmp(argv[i], "-sig")) || (!strcmp(argv[i], "-signal")))
544544+ {
545545+ if ((i + 1) >= argc) continue;
546546+ if (argv[i + 1][0] == '-') continue;
547547+548548+ i++;
549549+550550+ if ((argv[i][0] < '0') || (argv[i][1] > '9'))
551551+ {
552552+ fprintf(stderr, "-signal %s is invalid\n", argv[i]);
553553+ fprintf(stderr, "signals must be specified as integer values\n");
554554+ usage(name);
555555+ exit(1);
556556+ }
557557+558558+ }
559559+ else if (!strcmp(argv[i], "-dispatch"))
560560+ {}
561561+ else if (!strcmp(argv[i], "-check"))
562562+ {}
563563+ else if (!strcmp(argv[i], "-plain"))
564564+ {}
565565+ else if (!strcmp(argv[i], "-p"))
566566+ {
567567+ if ((i + 1) >= argc)
568568+ {
569569+ fprintf(stderr, "name required following -p\n");
570570+ usage(name);
571571+ exit(1);
572572+ }
573573+574574+ i++;
575575+ }
576576+ else if ((argv[i][0] == '-') && ((argv[i][1] == 'w') || ((argv[i][1] >= '0') && (argv[i][1] <= '9'))))
577577+ {
578578+ if ((i + 1) >= argc)
579579+ {
580580+ fprintf(stderr, "name required following %s\n", argv[i]);
581581+ usage(name);
582582+ exit(1);
583583+ }
584584+585585+ i++;
586586+ }
587587+ else if (!strcmp(argv[i], "-g"))
588588+ {
589589+ if ((i + 1) >= argc)
590590+ {
591591+ fprintf(stderr, "name required following -g\n");
592592+ usage(name);
593593+ exit(1);
594594+ }
595595+596596+ i++;
597597+ }
598598+ else if (!strcmp(argv[i], "-s"))
599599+ {
600600+ if ((i + 1) >= argc)
601601+ {
602602+ fprintf(stderr, "name required following -s\n");
603603+ usage(name);
604604+ exit(1);
605605+ }
606606+607607+ i++;
608608+609609+ if ((i + 1) >= argc)
610610+ {
611611+ fprintf(stderr, "value required following -s name\n");
612612+ usage(name);
613613+ exit(1);
614614+ }
615615+616616+ i++;
617617+ state = atoll(argv[i]);
618618+ if ((state == 0) && (strcmp(argv[i], "0")))
619619+ {
620620+ fprintf(stderr, "value following -s name must be a 64-bit integer\n");
621621+ }
622622+ }
623623+ else
624624+ {
625625+ fprintf(stderr, "unrecognized option: %s\n", argv[i]);
626626+ usage(name);
627627+ exit(1);
628628+ }
629629+ }
630630+631631+ if (opts != 0) notify_set_options(opts);
632632+633633+ for (i = 1; i < argc; i++)
634634+ {
635635+ if (!strcmp(argv[i], "-port"))
636636+ {
637637+ ntype = TYPE_PORT;
638638+ }
639639+ else if (!strcmp(argv[i], "-file"))
640640+ {
641641+ ntype = TYPE_FILE;
642642+ }
643643+ else if ((!strcmp(argv[i], "-sig")) || (!strcmp(argv[i], "-signal")))
644644+ {
645645+646646+ ntype = TYPE_SIGNAL;
647647+ if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
648648+ {
649649+ i++;
650650+ signum = atoi(argv[i]);
651651+ }
652652+ }
653653+ else if (!strcmp(argv[i], "-dispatch"))
654654+ {
655655+ ntype = TYPE_DISPATCH;
656656+ }
657657+ else if (!strcmp(argv[i], "-check"))
658658+ {
659659+ ntype = TYPE_CHECK;
660660+ }
661661+ else if (!strcmp(argv[i], "-plain"))
662662+ {
663663+ ntype = TYPE_PLAIN;
664664+ }
665665+ else if (!strcmp(argv[i], "-p"))
666666+ {
667667+ if ((i + 1) >= argc)
668668+ {
669669+ usage(name);
670670+ exit(1);
671671+ }
672672+673673+ i++;
674674+675675+ status = notify_post(argv[i]);
676676+ if (status != NOTIFY_STATUS_OK) printf("%s: %s\n", argv[i], notify_status_strerror(status));
677677+ else if (nap > 0) usleep(nap);
678678+ }
679679+ else if ((argv[i][0] == '-') && ((argv[i][1] == 'w') || ((argv[i][1] >= '0') && (argv[i][1] <= '9'))))
680680+ {
681681+ if ((i + 1) >= argc)
682682+ {
683683+ usage(name);
684684+ exit(1);
685685+ }
686686+687687+ n = IndexNull;
688688+ if (argv[i][1] != 'w') n = atoi(argv[i] + 1);
689689+690690+ i++;
691691+ tid = IndexNull;
692692+693693+ index = reg_find_name(argv[i]);
694694+ if (index != IndexNull)
695695+ {
696696+ fprintf(stderr, "Already watching for %s\n", argv[i]);
697697+ continue;
698698+ }
699699+700700+ status = do_register(argv[i], ntype, signum, n);
701701+ if (status != NOTIFY_STATUS_OK) printf("%s: %s\n", argv[i], notify_status_strerror(status));
702702+ }
703703+ else if (!strcmp(argv[i], "-g"))
704704+ {
705705+ if ((i + 1) >= argc)
706706+ {
707707+ usage(name);
708708+ exit(1);
709709+ }
710710+711711+ i++;
712712+ state = 0;
713713+ tid = IndexNull;
714714+715715+ status = notify_register_plain(argv[i], &tid);
716716+ if (status == NOTIFY_STATUS_OK)
717717+ {
718718+ status = notify_get_state(tid, &state);
719719+ notify_cancel(tid);
720720+ }
721721+722722+ if (status == NOTIFY_STATUS_OK) printf("%s %llu\n", argv[i], (unsigned long long)state);
723723+ else printf("%s: %s\n", argv[i], notify_status_strerror(status));
724724+ }
725725+ else if (!strcmp(argv[i], "-s"))
726726+ {
727727+ if ((i + 2) >= argc)
728728+ {
729729+ usage(name);
730730+ exit(1);
731731+ }
732732+733733+ i++;
734734+ tid = IndexNull;
735735+ status = notify_register_plain(argv[i], &tid);
736736+ if (status == NOTIFY_STATUS_OK)
737737+ {
738738+ state = atoll(argv[i + 1]);
739739+ status = notify_set_state(tid, state);
740740+ notify_cancel(tid);
741741+ }
742742+743743+ if (status != NOTIFY_STATUS_OK) printf("%s: %s\n", argv[i], notify_status_strerror(status));
744744+ i++;
745745+ }
746746+ }
747747+748748+ if (reg_count == 0) exit(0);
749749+750750+ dispatch_main();
751751+}
+763
libnotify/table.c
···11+/*
22+ * Copyright (c) 2003-2011 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#include <stdlib.h>
2525+#include <string.h>
2626+#include <syslog.h>
2727+#include <sys/types.h>
2828+#include <os/assumes.h>
2929+#include "table.h"
3030+3131+#define KEY_UNKNOWN 0
3232+#define KEY_INT 1
3333+#define KEY_STR_MINE 2
3434+#define KEY_STR_SHARED 3
3535+3636+#define DEFAULT_SIZE 256
3737+3838+typedef struct table_node_s
3939+{
4040+ union
4141+ {
4242+ char *string;
4343+ const char *const_string;
4444+ uint64_t uint64;
4545+ } key;
4646+ void *datum;
4747+ struct table_node_s *next;
4848+} table_node_t;
4949+5050+typedef struct __table_private
5151+{
5252+ uint32_t type;
5353+ uint32_t bucket_count;
5454+ table_node_t **bucket;
5555+} table_private_t;
5656+5757+typedef struct
5858+{
5959+ uint32_t bucket_index;
6060+ table_node_t *node;
6161+} table_traverse_t;
6262+6363+typedef struct __list_private
6464+{
6565+ struct __list_private *prev;
6666+ struct __list_private *next;
6767+ uint32_t refcount;
6868+ void *data;
6969+} list_private_t;
7070+7171+table_t *
7272+_nc_table_new(uint32_t n)
7373+{
7474+ table_private_t *t;
7575+7676+ t = (table_t *)malloc(sizeof(table_t));
7777+ if (t == NULL) return NULL;
7878+7979+ if (n == 0) n = DEFAULT_SIZE;
8080+8181+ t->type = KEY_UNKNOWN;
8282+ t->bucket_count = n;
8383+ t->bucket = (table_node_t **)calloc(t->bucket_count, sizeof(table_node_t *));
8484+ if (t->bucket == NULL)
8585+ {
8686+ free(t);
8787+ return NULL;
8888+ }
8989+9090+ return (table_t *)t;
9191+}
9292+9393+static uint32_t
9494+hash_key(int size, const char *key)
9595+{
9696+ uint32_t v;
9797+ char *p;
9898+9999+ if (key == NULL) return 0;
100100+101101+ v = 0;
102102+ for (p = (char *)key; *p != '\0'; p++)
103103+ {
104104+ v = (v << 1) ^ (v ^ *p);
105105+ }
106106+107107+ v %= size;
108108+ return v;
109109+}
110110+111111+static uint32_t
112112+hash_nkey(uint32_t size, uint64_t key)
113113+{
114114+ uint32_t x = key;
115115+ uint32_t y = key >> 32;
116116+ return ((x ^ y) % size);
117117+}
118118+119119+void *
120120+_nc_table_find_get_key(table_t *tin, const char *key, const char **shared_key)
121121+{
122122+ table_private_t *t;
123123+ table_node_t *n;
124124+ uint32_t b;
125125+126126+ if (tin == NULL) return NULL;
127127+ if (key == NULL) return NULL;
128128+129129+ if (shared_key != NULL) *shared_key = NULL;
130130+131131+ t = (table_private_t *)tin;
132132+ b = hash_key(t->bucket_count, key);
133133+134134+ for (n = t->bucket[b]; n != NULL; n = n->next)
135135+ {
136136+ if ((n->key.string != NULL) && (!strcmp(key, n->key.string)))
137137+ {
138138+ if (shared_key != NULL) *shared_key = n->key.const_string;
139139+ return n->datum;
140140+ }
141141+ }
142142+143143+ return NULL;
144144+}
145145+146146+void *
147147+_nc_table_find(table_t *tin, const char *key)
148148+{
149149+ return _nc_table_find_get_key(tin, key, NULL);
150150+}
151151+152152+void *
153153+_nc_table_find_64(table_t *tin, uint64_t key)
154154+{
155155+ table_private_t *t;
156156+ table_node_t *n;
157157+ uint32_t b;
158158+159159+ if (tin == NULL) return NULL;
160160+161161+ t = (table_private_t *)tin;
162162+ b = hash_nkey(t->bucket_count, key);
163163+164164+ for (n = t->bucket[b]; n != NULL; n = n->next)
165165+ {
166166+ if ((n->key.uint64 != (uint64_t)-1) && (key == n->key.uint64)) return n->datum;
167167+ }
168168+169169+ return NULL;
170170+}
171171+172172+void *
173173+_nc_table_find_n(table_t *tin, uint32_t key)
174174+{
175175+ uint64_t n64 = key;
176176+ return _nc_table_find_64(tin, n64);
177177+}
178178+179179+static void
180180+_nc_table_insert_type(table_t *tin, int type, char *key, const char *ckey, void *datum)
181181+{
182182+ table_private_t *t;
183183+ table_node_t *n;
184184+ uint32_t b;
185185+186186+ if (tin == NULL) return;
187187+ if ((key == NULL) && (ckey == NULL)) return;
188188+ if (datum == NULL) return;
189189+190190+ t = (table_private_t *)tin;
191191+ if (t->type == KEY_UNKNOWN) t->type = type;
192192+ else os_assumes(t->type == type);
193193+194194+ n = (table_node_t *)malloc(sizeof(table_node_t));
195195+196196+ if (key != NULL)
197197+ {
198198+ b = hash_key(t->bucket_count, key);
199199+ n->key.string = key;
200200+ }
201201+ else
202202+ {
203203+ b = hash_key(t->bucket_count, ckey);
204204+ n->key.const_string = ckey;
205205+ }
206206+207207+ n->datum = datum;
208208+ n->next = t->bucket[b];
209209+ t->bucket[b] = n;
210210+}
211211+212212+void
213213+_nc_table_insert(table_t *tin, const char *key, void *datum)
214214+{
215215+ char *dup;
216216+217217+ if (tin == NULL) return;
218218+ if (key == NULL) return;
219219+ if (datum == NULL) return;
220220+221221+ dup = strdup(key);
222222+ if (dup == NULL) return;
223223+224224+ _nc_table_insert_type(tin, KEY_STR_MINE, dup, NULL, datum);
225225+}
226226+227227+void
228228+_nc_table_insert_no_copy(table_t *tin, const char *key, void *datum)
229229+{
230230+ if (tin == NULL) return;
231231+ if (key == NULL) return;
232232+ if (datum == NULL) return;
233233+234234+ _nc_table_insert_type(tin, KEY_STR_SHARED, NULL, key, datum);
235235+}
236236+237237+void
238238+_nc_table_insert_pass(table_t *tin, char *key, void *datum)
239239+{
240240+ if (tin == NULL) return;
241241+ if (key == NULL) return;
242242+ if (datum == NULL) return;
243243+244244+ _nc_table_insert_type(tin, KEY_STR_MINE, key, NULL, datum);
245245+}
246246+247247+void
248248+_nc_table_insert_64(table_t *tin, uint64_t key, void *datum)
249249+{
250250+ table_private_t *t;
251251+ table_node_t *n;
252252+ uint32_t b;
253253+254254+ if (tin == NULL) return;
255255+ if (datum == NULL) return;
256256+257257+ t = (table_private_t *)tin;
258258+ if (t->type == KEY_UNKNOWN) t->type = KEY_INT;
259259+ else os_assumes(t->type == KEY_INT);
260260+261261+ b = hash_nkey(t->bucket_count, key);
262262+ n = (table_node_t *)malloc(sizeof(table_node_t));
263263+ n->key.uint64 = key;
264264+ n->datum = datum;
265265+ n->next = t->bucket[b];
266266+ t->bucket[b] = n;
267267+}
268268+269269+void
270270+_nc_table_insert_n(table_t *tin, uint32_t key, void *datum)
271271+{
272272+ uint64_t n64 = key;
273273+ _nc_table_insert_64(tin, n64, datum);
274274+}
275275+276276+void
277277+_nc_table_delete(table_t *tin, const char *key)
278278+{
279279+ table_private_t *t;
280280+ table_node_t *n, *p;
281281+ uint32_t b;
282282+283283+ if (tin == NULL) return;
284284+ if (key == NULL) return;
285285+286286+ t = (table_private_t *)tin;
287287+ os_assumes((t->type == KEY_STR_MINE) || (t->type == KEY_STR_SHARED));
288288+289289+ b = hash_key(t->bucket_count, key);
290290+291291+ p = NULL;
292292+ for (n = t->bucket[b]; n != NULL; n = n->next)
293293+ {
294294+ if ((n->key.string != NULL) && (!strcmp(key, n->key.string)))
295295+ {
296296+ if (p == NULL) t->bucket[b] = n->next;
297297+ else p->next = n->next;
298298+ if (t->type == KEY_STR_MINE) free(n->key.string);
299299+ free(n);
300300+ return;
301301+ }
302302+ p = n;
303303+ }
304304+}
305305+306306+void
307307+_nc_table_delete_64(table_t *tin, uint64_t key)
308308+{
309309+ table_private_t *t;
310310+ table_node_t *n, *p;
311311+ uint32_t b;
312312+313313+ if (tin == NULL) return;
314314+315315+ t = (table_private_t *)tin;
316316+ os_assumes(t->type == KEY_INT);
317317+318318+ b = hash_nkey(t->bucket_count, key);
319319+320320+ p = NULL;
321321+ for (n = t->bucket[b]; n != NULL; n = n->next)
322322+ {
323323+ if ((n->key.uint64 != (uint64_t)-1) && (key == n->key.uint64))
324324+ {
325325+ if (p == NULL) t->bucket[b] = n->next;
326326+ else p->next = n->next;
327327+ free(n);
328328+ return;
329329+ }
330330+ p = n;
331331+ }
332332+}
333333+334334+void
335335+_nc_table_delete_n(table_t *tin, uint32_t key)
336336+{
337337+ uint64_t n64 = key;
338338+ _nc_table_delete_64(tin, n64);
339339+}
340340+341341+void *
342342+_nc_table_traverse_start(table_t *tin)
343343+{
344344+ table_traverse_t *tt;
345345+ table_private_t *t;
346346+ uint32_t b;
347347+348348+ if (tin == NULL) return NULL;
349349+350350+ t = (table_private_t *)tin;
351351+ if (t->bucket_count == 0) return NULL;
352352+353353+ for (b = 0; b < t->bucket_count; b++)
354354+ {
355355+ if (t->bucket[b] != NULL)
356356+ {
357357+ tt = (table_traverse_t *)malloc(sizeof(table_traverse_t));
358358+ if (tt == NULL) return NULL;
359359+ tt->bucket_index = b;
360360+ tt->node = t->bucket[b];
361361+ return (void *)tt;
362362+ }
363363+ }
364364+365365+ return NULL;
366366+}
367367+368368+void *
369369+_nc_table_traverse(table_t *tin, void *ttin)
370370+{
371371+ table_private_t *t;
372372+ table_traverse_t *tt;
373373+ void *datum;
374374+ uint32_t b;
375375+376376+ if (tin == NULL) return NULL;
377377+ if (ttin == NULL) return NULL;
378378+379379+ t = (table_private_t *)tin;
380380+ tt = (table_traverse_t *)ttin;
381381+382382+ if (tt->node == NULL) return NULL;
383383+384384+ datum = tt->node->datum;
385385+ tt->node = tt->node->next;
386386+ if (tt->node != NULL) return datum;
387387+388388+ for (b = tt->bucket_index + 1; b < t->bucket_count; b++)
389389+ {
390390+ if (t->bucket[b] != NULL)
391391+ {
392392+ tt->bucket_index = b;
393393+ tt->node = t->bucket[b];
394394+ return datum;
395395+ }
396396+ }
397397+398398+ tt->bucket_index = b;
399399+ tt->node = NULL;
400400+401401+ return datum;
402402+}
403403+404404+void
405405+_nc_table_traverse_end(table_t *tin, void *ttin)
406406+{
407407+ if (ttin == NULL) return;
408408+ free(ttin);
409409+}
410410+411411+void
412412+_nc_table_free(table_t *tin)
413413+{
414414+ table_private_t *t;
415415+ table_node_t *n, *x;
416416+ uint32_t b;
417417+418418+ if (tin == NULL) return;
419419+420420+ t = (table_private_t *)tin;
421421+422422+ for (b = 0; b < t->bucket_count; b++)
423423+ {
424424+ x = NULL;
425425+ for (n = t->bucket[b]; n != NULL; n = x)
426426+ {
427427+ x = n->next;
428428+ if (t->type == KEY_STR_MINE) free(n->key.string);
429429+ free(n);
430430+ }
431431+ }
432432+433433+ free(t->bucket);
434434+ free(t);
435435+}
436436+437437+/* Linked List */
438438+439439+/*
440440+ * Make a new node
441441+ */
442442+list_t *
443443+_nc_list_new(void *d)
444444+{
445445+ list_t *n;
446446+447447+ n = (list_t *)calloc(1, sizeof(list_t));
448448+ if (n == NULL) return NULL;
449449+450450+ n->refcount = 1;
451451+ n->data = d;
452452+ return n;
453453+}
454454+455455+/*
456456+ * Release a node
457457+ */
458458+void
459459+_nc_list_release(list_t *l)
460460+{
461461+ if (l == NULL) return;
462462+ l->refcount--;
463463+ if (l->refcount > 0) return;
464464+465465+ free(l);
466466+}
467467+468468+/*
469469+ * Retain a node
470470+ */
471471+list_t *
472472+_nc_list_retain(list_t *l)
473473+{
474474+ if (l == NULL) return NULL;
475475+ l->refcount++;
476476+ return l;
477477+}
478478+479479+/*
480480+ * Retain a list
481481+ */
482482+list_t *
483483+_nc_list_retain_list(list_t *l)
484484+{
485485+ list_t *n;
486486+487487+ for (n = l; n != NULL; n = n->next) n->refcount++;
488488+ return l;
489489+}
490490+491491+/*
492492+ * Get previous node
493493+ */
494494+list_t *
495495+_nc_list_prev(list_t *l)
496496+{
497497+ if (l == NULL) return NULL;
498498+ return l->prev;
499499+}
500500+501501+/*
502502+ * Get next node
503503+ */
504504+list_t *
505505+_nc_list_next(list_t *l)
506506+{
507507+ if (l == NULL) return NULL;
508508+ return l->next;
509509+}
510510+511511+/*
512512+ * Get head (first node) of list
513513+ */
514514+list_t *
515515+_nc_list_head(list_t *l)
516516+{
517517+ list_t *p;
518518+519519+ if (l == NULL) return NULL;
520520+521521+ for (p = l; p->prev != NULL; p = p->prev);
522522+523523+ return p;
524524+}
525525+526526+/*
527527+ * Get tail (last node) of list
528528+ */
529529+list_t *
530530+_nc_list_tail(list_t *l)
531531+{
532532+ list_t *p;
533533+534534+ if (l == NULL) return NULL;
535535+536536+ for (p = l; p->next != NULL; p = p->next);
537537+538538+ return p;
539539+}
540540+541541+/*
542542+ * Insert a node in front of another node.
543543+ * Cuts list if n is NULL.
544544+ */
545545+list_t *
546546+_nc_list_prepend(list_t *l, list_t *n)
547547+{
548548+ if (l == NULL) return n;
549549+550550+ if (n != NULL)
551551+ {
552552+ n->next = l;
553553+ n->prev = l->prev;
554554+ }
555555+556556+ if (l->prev != NULL) l->prev->next = n;
557557+ l->prev = n;
558558+559559+ return n;
560560+}
561561+562562+/*
563563+ * Append a node after another node.
564564+ * Cuts list if n is NULL.
565565+ */
566566+list_t *
567567+_nc_list_append(list_t *l, list_t *n)
568568+{
569569+ if (l == NULL) return n;
570570+571571+ if (n != NULL)
572572+ {
573573+ n->prev = l;
574574+ n->next = l->next;
575575+ }
576576+577577+ if (l->next != NULL) n->next->prev = n;
578578+ l->next = n;
579579+580580+ return n;
581581+}
582582+583583+/*
584584+ * Set next pointer - use with care.
585585+ */
586586+void
587587+_nc_list_set_next(list_t *l, list_t *n)
588588+{
589589+ if (l == NULL) return;
590590+ l->next = n;
591591+}
592592+593593+/*
594594+ * Set prev pointer - use with care.
595595+ */
596596+void
597597+_nc_list_set_prev(list_t *l, list_t *p)
598598+{
599599+ if (l == NULL) return;
600600+ l->prev = p;
601601+}
602602+603603+/*
604604+ * Concatenate two lists.
605605+ * Returns new head.
606606+ */
607607+list_t *
608608+_nc_list_concat(list_t *a, list_t *b)
609609+{
610610+ list_t *p;
611611+612612+ if (a == NULL) return b;
613613+ if (b == NULL) return a;
614614+615615+ for (p = a; p->next != NULL; p = p->next);
616616+617617+ p->next = b;
618618+ b->prev = p;
619619+620620+ for (p = a; p->prev != NULL; p = p->prev);
621621+622622+ return p;
623623+}
624624+625625+uint32_t
626626+_nc_list_count(list_t *l)
627627+{
628628+ uint32_t n;
629629+ list_t *p;
630630+631631+ n = 0;
632632+ for (p = l; p != NULL; p = p->next) n++;
633633+ return n;
634634+}
635635+636636+void *
637637+_nc_list_data(list_t *l)
638638+{
639639+ if (l == NULL) return NULL;
640640+ return l->data;
641641+}
642642+643643+void
644644+_nc_list_set_data(list_t *l, void *d)
645645+{
646646+ if (l != NULL) l->data = d;
647647+}
648648+649649+list_t *
650650+_nc_list_find(list_t *l, void *d)
651651+{
652652+ list_t *p;
653653+654654+ if (l == NULL) return NULL;
655655+656656+ for (p = l; p != NULL; p = p->next)
657657+ {
658658+ if (p->data == d) return p;
659659+ }
660660+661661+ return NULL;
662662+}
663663+664664+list_t *
665665+_nc_list_find_release(list_t *l, void *d)
666666+{
667667+ list_t *p;
668668+669669+ if (l == NULL) return NULL;
670670+671671+ if (l->data == d)
672672+ {
673673+ p = l->next;
674674+ if (p != NULL) p->prev = NULL;
675675+ _nc_list_release(l);
676676+ return p;
677677+ }
678678+679679+ for (p = l->next; p != NULL; p = p->next)
680680+ {
681681+ if (p->data == d)
682682+ {
683683+ p->prev->next = p->next;
684684+ if (p->next != NULL) p->next->prev = p->prev;
685685+ _nc_list_release(p);
686686+ return l;
687687+ }
688688+ }
689689+690690+ return l;
691691+}
692692+693693+list_t *
694694+_nc_list_reverse(list_t *l)
695695+{
696696+ list_t *x, *s, *r;
697697+698698+ if (l == NULL) return NULL;
699699+700700+ x = l->prev;
701701+ r = l;
702702+ s = l->next;
703703+704704+ while (s != NULL)
705705+ {
706706+ s = r->next;
707707+ r->next = r->prev;
708708+ r->prev = s;
709709+ if (s != NULL) r = s;
710710+ }
711711+712712+ if (x != NULL)
713713+ {
714714+ x->next = r;
715715+ r->prev = x;
716716+ }
717717+718718+ return r;
719719+}
720720+721721+list_t *
722722+_nc_list_extract(list_t *n)
723723+{
724724+ if (n == NULL) return NULL;
725725+726726+ if (n->prev != NULL) n->prev->next = n->next;
727727+ if (n->next != NULL) n->next->prev = n->prev;
728728+729729+ n->prev = NULL;
730730+ n->next = NULL;
731731+732732+ return n;
733733+}
734734+735735+list_t *
736736+_nc_list_chop(list_t *l)
737737+{
738738+ list_t *p;
739739+740740+ if (l == NULL) return NULL;
741741+ p = l->next;
742742+ if (p != NULL) p->prev = NULL;
743743+744744+ _nc_list_release(l);
745745+ return p;
746746+}
747747+748748+void
749749+_nc_list_release_list(list_t *l)
750750+{
751751+ list_t *p, *n;
752752+753753+ if (l == NULL) return;
754754+ if (l->prev != NULL) l->prev->next = NULL;
755755+756756+ p = l;
757757+ while (p != NULL)
758758+ {
759759+ n = p->next;
760760+ _nc_list_release(p);
761761+ p = n;
762762+ }
763763+}
+88
libnotify/table.h
···11+/*
22+ * Copyright (c) 2003-2011 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#ifndef _NOTIFY_TABLE_H_
2525+#define _NOTIFY_TABLE_H_
2626+2727+#include <stdint.h>
2828+2929+typedef struct __table_private table_t;
3030+typedef struct __list_private list_t;
3131+3232+extern table_t *_nc_table_new(uint32_t n);
3333+3434+extern void _nc_table_insert(table_t *t, const char *key, void *datum);
3535+extern void _nc_table_insert_no_copy(table_t *t, const char *key, void *datum);
3636+extern void _nc_table_insert_pass(table_t *t, char *key, void *datum);
3737+extern void _nc_table_insert_n(table_t *t, uint32_t key, void *datum);
3838+extern void _nc_table_insert_64(table_t *t, uint64_t key, void *datum);
3939+4040+extern void *_nc_table_find(table_t *t, const char *key);
4141+extern void *_nc_table_find_get_key(table_t *tin, const char *key, const char **shared_key);
4242+extern void *_nc_table_find_n(table_t *t, uint32_t key);
4343+extern void *_nc_table_find_64(table_t *t, uint64_t key);
4444+4545+extern void _nc_table_delete(table_t *t, const char *key);
4646+extern void _nc_table_delete_n(table_t *t, uint32_t key);
4747+extern void _nc_table_delete_64(table_t *t, uint64_t key);
4848+4949+extern void *_nc_table_traverse_start(table_t *tin);
5050+extern void *_nc_table_traverse(table_t *tin, void *ttin);
5151+extern void _nc_table_traverse_end(table_t *tin, void *ttin);
5252+5353+extern void _nc_table_free(table_t *tin);
5454+5555+extern list_t *_nc_list_new(void *d);
5656+5757+extern list_t *_nc_list_retain(list_t *l);
5858+extern list_t *_nc_list_retain_list(list_t *l);
5959+6060+extern void _nc_list_release(list_t *l);
6161+extern void _nc_list_release_list(list_t *l);
6262+6363+extern list_t *_nc_list_prev(list_t *l);
6464+extern list_t *_nc_list_next(list_t *l);
6565+6666+extern void _nc_list_set_next(list_t *l, list_t *n);
6767+extern void _nc_list_set_prev(list_t *l, list_t *p);
6868+6969+extern list_t *_nc_list_head(list_t *l);
7070+extern list_t *_nc_list_tail(list_t *l);
7171+7272+extern list_t *_nc_list_prepend(list_t *l, list_t *n);
7373+extern list_t *_nc_list_append(list_t *l, list_t *n);
7474+7575+extern list_t *_nc_list_concat(list_t *a, list_t *b);
7676+7777+extern void *_nc_list_data(list_t *l);
7878+extern void _nc_list_set_data(list_t *l, void *d);
7979+8080+extern list_t *_nc_list_find(list_t *l, void *d);
8181+extern list_t *_nc_list_find_release(list_t *l, void *d);
8282+8383+extern list_t * _nc_list_reverse(list_t *l);
8484+extern uint32_t _nc_list_count(list_t *l);
8585+extern list_t *_nc_list_extract(list_t *n);
8686+extern list_t *_nc_list_chop(list_t *l);
8787+8888+#endif /* _NOTIFY_TABLE_H_ */
···11+/* -*- Mode: C; tab-width: 4 -*-
22+ *
33+ * Copyright (c) 2003-2013 Apple Computer, Inc. All rights reserved.
44+ *
55+ * Redistribution and use in source and binary forms, with or without
66+ * modification, are permitted provided that the following conditions are met:
77+ *
88+ * 1. Redistributions of source code must retain the above copyright notice,
99+ * this list of conditions and the following disclaimer.
1010+ * 2. Redistributions in binary form must reproduce the above copyright notice,
1111+ * this list of conditions and the following disclaimer in the documentation
1212+ * and/or other materials provided with the distribution.
1313+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
1414+ * contributors may be used to endorse or promote products derived from this
1515+ * software without specific prior written permission.
1616+ *
1717+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
1818+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1919+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2020+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
2121+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2222+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2323+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2424+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2525+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2626+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2727+ */
2828+2929+3030+/*! @header DNS Service Discovery
3131+ *
3232+ * @discussion This section describes the functions, callbacks, and data structures
3333+ * that make up the DNS Service Discovery API.
3434+ *
3535+ * The DNS Service Discovery API is part of Bonjour, Apple's implementation
3636+ * of zero-configuration networking (ZEROCONF).
3737+ *
3838+ * Bonjour allows you to register a network service, such as a
3939+ * printer or file server, so that it can be found by name or browsed
4040+ * for by service type and domain. Using Bonjour, applications can
4141+ * discover what services are available on the network, along with
4242+ * all the information -- such as name, IP address, and port --
4343+ * necessary to access a particular service.
4444+ *
4545+ * In effect, Bonjour combines the functions of a local DNS server and
4646+ * AppleTalk. Bonjour allows applications to provide user-friendly printer
4747+ * and server browsing, among other things, over standard IP networks.
4848+ * This behavior is a result of combining protocols such as multicast and
4949+ * DNS to add new functionality to the network (such as multicast DNS).
5050+ *
5151+ * Bonjour gives applications easy access to services over local IP
5252+ * networks without requiring the service or the application to support
5353+ * an AppleTalk or a Netbeui stack, and without requiring a DNS server
5454+ * for the local network.
5555+ */
5656+5757+/* _DNS_SD_H contains the API version number for this header file
5858+ * The API version defined in this header file symbol allows for compile-time
5959+ * checking, so that C code building with earlier versions of the header file
6060+ * can avoid compile errors trying to use functions that aren't even defined
6161+ * in those earlier versions. Similar checks may also be performed at run-time:
6262+ * => weak linking -- to avoid link failures if run with an earlier
6363+ * version of the library that's missing some desired symbol, or
6464+ * => DNSServiceGetProperty(DaemonVersion) -- to verify whether the running daemon
6565+ * ("system service" on Windows) meets some required minimum functionality level.
6666+ */
6767+6868+#ifndef _DNS_SD_H
6969+#define _DNS_SD_H 5610101
7070+7171+#ifdef __cplusplus
7272+extern "C" {
7373+#endif
7474+7575+/* Set to 1 if libdispatch is supported
7676+ * Note: May also be set by project and/or Makefile
7777+ */
7878+#ifndef _DNS_SD_LIBDISPATCH
7979+#define _DNS_SD_LIBDISPATCH 1
8080+#endif /* ndef _DNS_SD_LIBDISPATCH */
8181+8282+/* standard calling convention under Win32 is __stdcall */
8383+/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */
8484+/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */
8585+#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64)
8686+#define DNSSD_API __stdcall
8787+#else
8888+#define DNSSD_API
8989+#endif
9090+9191+/* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */
9292+#if defined(__FreeBSD__) && (__FreeBSD__ < 5)
9393+#include <sys/types.h>
9494+9595+/* Likewise, on Sun, standard integer types are in sys/types.h */
9696+#elif defined(__sun__)
9797+#include <sys/types.h>
9898+9999+/* EFI does not have stdint.h, or anything else equivalent */
100100+#elif defined(EFI32) || defined(EFI64) || defined(EFIX64)
101101+#include "Tiano.h"
102102+#if !defined(_STDINT_H_)
103103+typedef UINT8 uint8_t;
104104+typedef INT8 int8_t;
105105+typedef UINT16 uint16_t;
106106+typedef INT16 int16_t;
107107+typedef UINT32 uint32_t;
108108+typedef INT32 int32_t;
109109+#endif
110110+/* Windows has its own differences */
111111+#elif defined(_WIN32)
112112+#include <windows.h>
113113+#define _UNUSED
114114+#ifndef _MSL_STDINT_H
115115+typedef UINT8 uint8_t;
116116+typedef INT8 int8_t;
117117+typedef UINT16 uint16_t;
118118+typedef INT16 int16_t;
119119+typedef UINT32 uint32_t;
120120+typedef INT32 int32_t;
121121+#endif
122122+123123+/* All other Posix platforms use stdint.h */
124124+#else
125125+#include <stdint.h>
126126+#endif
127127+128128+#if _DNS_SD_LIBDISPATCH
129129+#include <dispatch/dispatch.h>
130130+#endif
131131+132132+/* DNSServiceRef, DNSRecordRef
133133+ *
134134+ * Opaque internal data types.
135135+ * Note: client is responsible for serializing access to these structures if
136136+ * they are shared between concurrent threads.
137137+ */
138138+139139+typedef struct _DNSServiceRef_t *DNSServiceRef;
140140+typedef struct _DNSRecordRef_t *DNSRecordRef;
141141+142142+struct sockaddr;
143143+144144+/*! @enum General flags
145145+ * Most DNS-SD API functions and callbacks include a DNSServiceFlags parameter.
146146+ * As a general rule, any given bit in the 32-bit flags field has a specific fixed meaning,
147147+ * regardless of the function or callback being used. For any given function or callback,
148148+ * typically only a subset of the possible flags are meaningful, and all others should be zero.
149149+ * The discussion section for each API call describes which flags are valid for that call
150150+ * and callback. In some cases, for a particular call, it may be that no flags are currently
151151+ * defined, in which case the DNSServiceFlags parameter exists purely to allow future expansion.
152152+ * In all cases, developers should expect that in future releases, it is possible that new flag
153153+ * values will be defined, and write code with this in mind. For example, code that tests
154154+ * if (flags == kDNSServiceFlagsAdd) ...
155155+ * will fail if, in a future release, another bit in the 32-bit flags field is also set.
156156+ * The reliable way to test whether a particular bit is set is not with an equality test,
157157+ * but with a bitwise mask:
158158+ * if (flags & kDNSServiceFlagsAdd) ...
159159+ * With the exception of kDNSServiceFlagsValidate, each flag can be valid(be set)
160160+ * EITHER only as an input to one of the DNSService*() APIs OR only as an output
161161+ * (provide status) through any of the callbacks used. For example, kDNSServiceFlagsAdd
162162+ * can be set only as an output in the callback, whereas the kDNSServiceFlagsIncludeP2P
163163+ * can be set only as an input to the DNSService*() APIs. See comments on kDNSServiceFlagsValidate
164164+ * defined in enum below.
165165+ */
166166+enum
167167+{
168168+ kDNSServiceFlagsMoreComing = 0x1,
169169+ /* MoreComing indicates to a callback that at least one more result is
170170+ * queued and will be delivered following immediately after this one.
171171+ * When the MoreComing flag is set, applications should not immediately
172172+ * update their UI, because this can result in a great deal of ugly flickering
173173+ * on the screen, and can waste a great deal of CPU time repeatedly updating
174174+ * the screen with content that is then immediately erased, over and over.
175175+ * Applications should wait until MoreComing is not set, and then
176176+ * update their UI when no more changes are imminent.
177177+ * When MoreComing is not set, that doesn't mean there will be no more
178178+ * answers EVER, just that there are no more answers immediately
179179+ * available right now at this instant. If more answers become available
180180+ * in the future they will be delivered as usual.
181181+ */
182182+183183+ kDNSServiceFlagsAdd = 0x2,
184184+ kDNSServiceFlagsDefault = 0x4,
185185+ /* Flags for domain enumeration and browse/query reply callbacks.
186186+ * "Default" applies only to enumeration and is only valid in
187187+ * conjunction with "Add". An enumeration callback with the "Add"
188188+ * flag NOT set indicates a "Remove", i.e. the domain is no longer
189189+ * valid.
190190+ */
191191+192192+ kDNSServiceFlagsNoAutoRename = 0x8,
193193+ /* Flag for specifying renaming behavior on name conflict when registering
194194+ * non-shared records. By default, name conflicts are automatically handled
195195+ * by renaming the service. NoAutoRename overrides this behavior - with this
196196+ * flag set, name conflicts will result in a callback. The NoAutorename flag
197197+ * is only valid if a name is explicitly specified when registering a service
198198+ * (i.e. the default name is not used.)
199199+ */
200200+201201+ kDNSServiceFlagsShared = 0x10,
202202+ kDNSServiceFlagsUnique = 0x20,
203203+ /* Flag for registering individual records on a connected
204204+ * DNSServiceRef. Shared indicates that there may be multiple records
205205+ * with this name on the network (e.g. PTR records). Unique indicates that the
206206+ * record's name is to be unique on the network (e.g. SRV records).
207207+ */
208208+209209+ kDNSServiceFlagsBrowseDomains = 0x40,
210210+ kDNSServiceFlagsRegistrationDomains = 0x80,
211211+ /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains.
212212+ * BrowseDomains enumerates domains recommended for browsing, RegistrationDomains
213213+ * enumerates domains recommended for registration.
214214+ */
215215+216216+ kDNSServiceFlagsLongLivedQuery = 0x100,
217217+ /* Flag for creating a long-lived unicast query for the DNSServiceQueryRecord call. */
218218+219219+ kDNSServiceFlagsAllowRemoteQuery = 0x200,
220220+ /* Flag for creating a record for which we will answer remote queries
221221+ * (queries from hosts more than one hop away; hosts not directly connected to the local link).
222222+ */
223223+224224+ kDNSServiceFlagsForceMulticast = 0x400,
225225+ /* Flag for signifying that a query or registration should be performed exclusively via multicast
226226+ * DNS, even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS.
227227+ */
228228+229229+ kDNSServiceFlagsForce = 0x800, // This flag is deprecated.
230230+231231+ kDNSServiceFlagsKnownUnique = 0x800,
232232+ /*
233233+ * Client guarantees that record names are unique, so we can skip sending out initial
234234+ * probe messages. Standard name conflict resolution is still done if a conflict is discovered.
235235+ * Currently only valid for a DNSServiceRegister call.
236236+ */
237237+238238+ kDNSServiceFlagsReturnIntermediates = 0x1000,
239239+ /* Flag for returning intermediate results.
240240+ * For example, if a query results in an authoritative NXDomain (name does not exist)
241241+ * then that result is returned to the client. However the query is not implicitly
242242+ * cancelled -- it remains active and if the answer subsequently changes
243243+ * (e.g. because a VPN tunnel is subsequently established) then that positive
244244+ * result will still be returned to the client.
245245+ * Similarly, if a query results in a CNAME record, then in addition to following
246246+ * the CNAME referral, the intermediate CNAME result is also returned to the client.
247247+ * When this flag is not set, NXDomain errors are not returned, and CNAME records
248248+ * are followed silently without informing the client of the intermediate steps.
249249+ * (In earlier builds this flag was briefly calledkDNSServiceFlagsReturnCNAME)
250250+ */
251251+252252+ kDNSServiceFlagsNonBrowsable = 0x2000,
253253+ /* A service registered with the NonBrowsable flag set can be resolved using
254254+ * DNSServiceResolve(), but will not be discoverable using DNSServiceBrowse().
255255+ * This is for cases where the name is actually a GUID; it is found by other means;
256256+ * there is no end-user benefit to browsing to find a long list of opaque GUIDs.
257257+ * Using the NonBrowsable flag creates SRV+TXT without the cost of also advertising
258258+ * an associated PTR record.
259259+ */
260260+261261+ kDNSServiceFlagsShareConnection = 0x4000,
262262+ /* For efficiency, clients that perform many concurrent operations may want to use a
263263+ * single Unix Domain Socket connection with the background daemon, instead of having a
264264+ * separate connection for each independent operation. To use this mode, clients first
265265+ * call DNSServiceCreateConnection(&MainRef) to initialize the main DNSServiceRef.
266266+ * For each subsequent operation that is to share that same connection, the client copies
267267+ * the MainRef, and then passes the address of that copy, setting the ShareConnection flag
268268+ * to tell the library that this DNSServiceRef is not a typical uninitialized DNSServiceRef;
269269+ * it's a copy of an existing DNSServiceRef whose connection information should be reused.
270270+ *
271271+ * For example:
272272+ *
273273+ * DNSServiceErrorType error;
274274+ * DNSServiceRef MainRef;
275275+ * error = DNSServiceCreateConnection(&MainRef);
276276+ * if (error) ...
277277+ * DNSServiceRef BrowseRef = MainRef; // Important: COPY the primary DNSServiceRef first...
278278+ * error = DNSServiceBrowse(&BrowseRef, kDNSServiceFlagsShareConnection, ...); // then use the copy
279279+ * if (error) ...
280280+ * ...
281281+ * DNSServiceRefDeallocate(BrowseRef); // Terminate the browse operation
282282+ * DNSServiceRefDeallocate(MainRef); // Terminate the shared connection
283283+ * Also see Point 4.(Don't Double-Deallocate if the MainRef has been Deallocated) in Notes below:
284284+ *
285285+ * Notes:
286286+ *
287287+ * 1. Collective kDNSServiceFlagsMoreComing flag
288288+ * When callbacks are invoked using a shared DNSServiceRef, the
289289+ * kDNSServiceFlagsMoreComing flag applies collectively to *all* active
290290+ * operations sharing the same parent DNSServiceRef. If the MoreComing flag is
291291+ * set it means that there are more results queued on this parent DNSServiceRef,
292292+ * but not necessarily more results for this particular callback function.
293293+ * The implication of this for client programmers is that when a callback
294294+ * is invoked with the MoreComing flag set, the code should update its
295295+ * internal data structures with the new result, and set a variable indicating
296296+ * that its UI needs to be updated. Then, later when a callback is eventually
297297+ * invoked with the MoreComing flag not set, the code should update *all*
298298+ * stale UI elements related to that shared parent DNSServiceRef that need
299299+ * updating, not just the UI elements related to the particular callback
300300+ * that happened to be the last one to be invoked.
301301+ *
302302+ * 2. Canceling operations and kDNSServiceFlagsMoreComing
303303+ * Whenever you cancel any operation for which you had deferred UI updates
304304+ * waiting because of a kDNSServiceFlagsMoreComing flag, you should perform
305305+ * those deferred UI updates. This is because, after cancelling the operation,
306306+ * you can no longer wait for a callback *without* MoreComing set, to tell
307307+ * you do perform your deferred UI updates (the operation has been canceled,
308308+ * so there will be no more callbacks). An implication of the collective
309309+ * kDNSServiceFlagsMoreComing flag for shared connections is that this
310310+ * guideline applies more broadly -- any time you cancel an operation on
311311+ * a shared connection, you should perform all deferred UI updates for all
312312+ * operations sharing that connection. This is because the MoreComing flag
313313+ * might have been referring to events coming for the operation you canceled,
314314+ * which will now not be coming because the operation has been canceled.
315315+ *
316316+ * 3. Only share DNSServiceRef's created with DNSServiceCreateConnection
317317+ * Calling DNSServiceCreateConnection(&ref) creates a special shareable DNSServiceRef.
318318+ * DNSServiceRef's created by other calls like DNSServiceBrowse() or DNSServiceResolve()
319319+ * cannot be shared by copying them and using kDNSServiceFlagsShareConnection.
320320+ *
321321+ * 4. Don't Double-Deallocate if the MainRef has been Deallocated
322322+ * Calling DNSServiceRefDeallocate(ref) for a particular operation's DNSServiceRef terminates
323323+ * just that operation. Calling DNSServiceRefDeallocate(ref) for the main shared DNSServiceRef
324324+ * (the parent DNSServiceRef, originally created by DNSServiceCreateConnection(&ref))
325325+ * automatically terminates the shared connection and all operations that were still using it.
326326+ * After doing this, DO NOT then attempt to deallocate any remaining subordinate DNSServiceRef's.
327327+ * The memory used by those subordinate DNSServiceRef's has already been freed, so any attempt
328328+ * to do a DNSServiceRefDeallocate (or any other operation) on them will result in accesses
329329+ * to freed memory, leading to crashes or other equally undesirable results.
330330+ *
331331+ * 5. Thread Safety
332332+ * The dns_sd.h API does not presuppose any particular threading model, and consequently
333333+ * does no locking of its own (which would require linking some specific threading library).
334334+ * If client code calls API routines on the same DNSServiceRef concurrently
335335+ * from multiple threads, it is the client's responsibility to use a mutext
336336+ * lock or take similar appropriate precautions to serialize those calls.
337337+ */
338338+339339+ kDNSServiceFlagsSuppressUnusable = 0x8000,
340340+ /*
341341+ * This flag is meaningful only in DNSServiceQueryRecord which suppresses unusable queries on the
342342+ * wire. If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name)
343343+ * but this host has no routable IPv6 address, then the call will not try to look up IPv6 addresses
344344+ * for "hostname", since any addresses it found would be unlikely to be of any use anyway. Similarly,
345345+ * if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for
346346+ * "hostname".
347347+ */
348348+349349+ kDNSServiceFlagsTimeout = 0x10000,
350350+ /*
351351+ * When kDNServiceFlagsTimeout is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, the query is
352352+ * stopped after a certain number of seconds have elapsed. The time at which the query will be stopped
353353+ * is determined by the system and cannot be configured by the user. The query will be stopped irrespective
354354+ * of whether a response was given earlier or not. When the query is stopped, the callback will be called
355355+ * with an error code of kDNSServiceErr_Timeout and a NULL sockaddr will be returned for DNSServiceGetAddrInfo
356356+ * and zero length rdata will be returned for DNSServiceQueryRecord.
357357+ */
358358+359359+ kDNSServiceFlagsIncludeP2P = 0x20000,
360360+ /*
361361+ * Include P2P interfaces when kDNSServiceInterfaceIndexAny is specified.
362362+ * By default, specifying kDNSServiceInterfaceIndexAny does not include P2P interfaces.
363363+ */
364364+365365+ kDNSServiceFlagsWakeOnResolve = 0x40000,
366366+ /*
367367+ * This flag is meaningful only in DNSServiceResolve. When set, it tries to send a magic packet
368368+ * to wake up the client.
369369+ */
370370+371371+ kDNSServiceFlagsBackgroundTrafficClass = 0x80000,
372372+ /*
373373+ * This flag is meaningful for Unicast DNS queries. When set, it uses the background traffic
374374+ * class for packets that service the request.
375375+ */
376376+377377+ kDNSServiceFlagsIncludeAWDL = 0x100000,
378378+ /*
379379+ * Include AWDL interface when kDNSServiceInterfaceIndexAny is specified.
380380+ */
381381+382382+ kDNSServiceFlagsValidate = 0x200000,
383383+ /*
384384+ * This flag is meaningful in DNSServiceGetAddrInfo and DNSServiceQueryRecord. This is the ONLY flag to be valid
385385+ * as an input to the APIs and also an output through the callbacks in the APIs.
386386+ *
387387+ * When this flag is passed to DNSServiceQueryRecord and DNSServiceGetAddrInfo to resolve unicast names,
388388+ * the response will be validated using DNSSEC. The validation results are delivered using the flags field in
389389+ * the callback and kDNSServiceFlagsValidate is marked in the flags to indicate that DNSSEC status is also available.
390390+ * When the callback is called to deliver the query results, the validation results may or may not be available.
391391+ * If it is not delivered along with the results, the validation status is delivered when the validation completes.
392392+ *
393393+ * When the validation results are delivered in the callback, it is indicated by marking the flags with
394394+ * kDNSServiceFlagsValidate and kDNSServiceFlagsAdd along with the DNSSEC status flags (described below) and a NULL
395395+ * sockaddr will be returned for DNSServiceGetAddrInfo and zero length rdata will be returned for DNSServiceQueryRecord.
396396+ * DNSSEC validation results are for the whole RRSet and not just individual records delivered in the callback. When
397397+ * kDNSServiceFlagsAdd is not set in the flags, applications should implicitly assume that the DNSSEC status of the
398398+ * RRSet that has been delivered up until that point is not valid anymore, till another callback is called with
399399+ * kDNSServiceFlagsAdd and kDNSServiceFlagsValidate.
400400+ *
401401+ * The following four flags indicate the status of the DNSSEC validation and marked in the flags field of the callback.
402402+ * When any of the four flags is set, kDNSServiceFlagsValidate will also be set. To check the validation status, the
403403+ * other applicable output flags should be masked. See kDNSServiceOutputFlags below.
404404+ */
405405+406406+ kDNSServiceFlagsSecure = 0x200010,
407407+ /*
408408+ * The response has been validated by verifying all the signaures in the response and was able to
409409+ * build a successful authentication chain starting from a known trust anchor.
410410+ */
411411+412412+ kDNSServiceFlagsInsecure = 0x200020,
413413+ /*
414414+ * A chain of trust cannot be built starting from a known trust anchor to the response.
415415+ */
416416+417417+ kDNSServiceFlagsBogus = 0x200040,
418418+ /*
419419+ * If the response cannot be verified to be secure due to expired signatures, missing signatures etc.,
420420+ * then the results are considered to be bogus.
421421+ */
422422+423423+ kDNSServiceFlagsIndeterminate = 0x200080,
424424+ /*
425425+ * There is no valid trust anchor that can be used to determine whether a response is secure or not.
426426+ */
427427+428428+ kDNSServiceFlagsUnicastResponse = 0x400000,
429429+ /*
430430+ * Request unicast response to query.
431431+ */
432432+ kDNSServiceFlagsValidateOptional = 0x800000,
433433+434434+ /*
435435+ * This flag is identical to kDNSServiceFlagsValidate except for the case where the response
436436+ * cannot be validated. If this flag is set in DNSServiceQueryRecord or DNSServiceGetAddrInfo,
437437+ * the DNSSEC records will be requested for validation. If they cannot be received for some reason
438438+ * during the validation (e.g., zone is not signed, zone is signed but cannot be traced back to
439439+ * root, recursive server does not understand DNSSEC etc.), then this will fallback to the default
440440+ * behavior where the validation will not be performed and no DNSSEC results will be provided.
441441+ *
442442+ * If the zone is signed and there is a valid path to a known trust anchor configured in the system
443443+ * and the application requires DNSSEC validation irrespective of the DNSSEC awareness in the current
444444+ * network, then this option MUST not be used. This is only intended to be used during the transition
445445+ * period where the different nodes participating in the DNS resolution may not understand DNSSEC or
446446+ * managed properly (e.g. missing DS record) but still want to be able to resolve DNS successfully.
447447+ */
448448+449449+ kDNSServiceFlagsWakeOnlyService = 0x1000000,
450450+ /*
451451+ * This flag is meaningful only in DNSServiceRegister. When set, the service will not be registered
452452+ * with sleep proxy server during sleep.
453453+ */
454454+455455+ kDNSServiceFlagsThresholdOne = 0x2000000,
456456+ kDNSServiceFlagsThresholdFinder = 0x4000000,
457457+ kDNSServiceFlagsThresholdReached = kDNSServiceFlagsThresholdOne,
458458+ /*
459459+ * kDNSServiceFlagsThresholdOne is meaningful only in DNSServiceBrowse. When set,
460460+ * the system will stop issuing browse queries on the network once the number
461461+ * of answers returned is one or more. It will issue queries on the network
462462+ * again if the number of answers drops to zero.
463463+ * This flag is for Apple internal use only. Third party developers
464464+ * should not rely on this behavior being supported in any given software release.
465465+ *
466466+ * kDNSServiceFlagsThresholdFinder is meaningful only in DNSServiceBrowse. When set,
467467+ * the system will stop issuing browse queries on the network once the number
468468+ * of answers has reached the threshold set for Finder.
469469+ * It will issue queries on the network again if the number of answers drops below
470470+ * this threshold.
471471+ * This flag is for Apple internal use only. Third party developers
472472+ * should not rely on this behavior being supported in any given software release.
473473+ *
474474+ * When kDNSServiceFlagsThresholdReached is set in the client callback add or remove event,
475475+ * it indicates that the browse answer threshold has been reached and no
476476+ * browse requests will be generated on the network until the number of answers falls
477477+ * below the threshold value. Add and remove events can still occur based
478478+ * on incoming Bonjour traffic observed by the system.
479479+ * The set of services return to the client is not guaranteed to represent the
480480+ * entire set of services present on the network once the threshold has been reached.
481481+ *
482482+ * Note, while kDNSServiceFlagsThresholdReached and kDNSServiceFlagsThresholdOne
483483+ * have the same value, there isn't a conflict because kDNSServiceFlagsThresholdReached
484484+ * is only set in the callbacks and kDNSServiceFlagsThresholdOne is only set on
485485+ * input to a DNSServiceBrowse call.
486486+ */
487487+ kDNSServiceFlagsDenyCellular = 0x8000000,
488488+ /*
489489+ * This flag is meaningful only for Unicast DNS queries. When set, the kernel will restrict
490490+ * DNS resolutions on the cellular interface for that request.
491491+ */
492492+493493+ kDNSServiceFlagsServiceIndex = 0x10000000,
494494+ /*
495495+ * This flag is meaningful only for DNSServiceGetAddrInfo() for Unicast DNS queries.
496496+ * When set, DNSServiceGetAddrInfo() will interpret the "interfaceIndex" argument of the call
497497+ * as the "serviceIndex".
498498+ */
499499+500500+ kDNSServiceFlagsDenyExpensive = 0x20000000
501501+ /*
502502+ * This flag is meaningful only for Unicast DNS queries. When set, the kernel will restrict
503503+ * DNS resolutions on interfaces defined as expensive for that request.
504504+ */
505505+506506+};
507507+508508+#define kDNSServiceOutputFlags (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional | kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault)
509509+ /* All the output flags excluding the DNSSEC Status flags. Typically used to check DNSSEC Status */
510510+511511+/* Possible protocol values */
512512+enum
513513+{
514514+ /* for DNSServiceGetAddrInfo() */
515515+ kDNSServiceProtocol_IPv4 = 0x01,
516516+ kDNSServiceProtocol_IPv6 = 0x02,
517517+ /* 0x04 and 0x08 reserved for future internetwork protocols */
518518+519519+ /* for DNSServiceNATPortMappingCreate() */
520520+ kDNSServiceProtocol_UDP = 0x10,
521521+ kDNSServiceProtocol_TCP = 0x20
522522+ /* 0x40 and 0x80 reserved for future transport protocols, e.g. SCTP [RFC 2960]
523523+ * or DCCP [RFC 4340]. If future NAT gateways are created that support port
524524+ * mappings for these protocols, new constants will be defined here.
525525+ */
526526+};
527527+528528+/*
529529+ * The values for DNS Classes and Types are listed in RFC 1035, and are available
530530+ * on every OS in its DNS header file. Unfortunately every OS does not have the
531531+ * same header file containing DNS Class and Type constants, and the names of
532532+ * the constants are not consistent. For example, BIND 8 uses "T_A",
533533+ * BIND 9 uses "ns_t_a", Windows uses "DNS_TYPE_A", etc.
534534+ * For this reason, these constants are also listed here, so that code using
535535+ * the DNS-SD programming APIs can use these constants, so that the same code
536536+ * can compile on all our supported platforms.
537537+ */
538538+539539+enum
540540+{
541541+ kDNSServiceClass_IN = 1 /* Internet */
542542+};
543543+544544+enum
545545+{
546546+ kDNSServiceType_A = 1, /* Host address. */
547547+ kDNSServiceType_NS = 2, /* Authoritative server. */
548548+ kDNSServiceType_MD = 3, /* Mail destination. */
549549+ kDNSServiceType_MF = 4, /* Mail forwarder. */
550550+ kDNSServiceType_CNAME = 5, /* Canonical name. */
551551+ kDNSServiceType_SOA = 6, /* Start of authority zone. */
552552+ kDNSServiceType_MB = 7, /* Mailbox domain name. */
553553+ kDNSServiceType_MG = 8, /* Mail group member. */
554554+ kDNSServiceType_MR = 9, /* Mail rename name. */
555555+ kDNSServiceType_NULL = 10, /* Null resource record. */
556556+ kDNSServiceType_WKS = 11, /* Well known service. */
557557+ kDNSServiceType_PTR = 12, /* Domain name pointer. */
558558+ kDNSServiceType_HINFO = 13, /* Host information. */
559559+ kDNSServiceType_MINFO = 14, /* Mailbox information. */
560560+ kDNSServiceType_MX = 15, /* Mail routing information. */
561561+ kDNSServiceType_TXT = 16, /* One or more text strings (NOT "zero or more..."). */
562562+ kDNSServiceType_RP = 17, /* Responsible person. */
563563+ kDNSServiceType_AFSDB = 18, /* AFS cell database. */
564564+ kDNSServiceType_X25 = 19, /* X_25 calling address. */
565565+ kDNSServiceType_ISDN = 20, /* ISDN calling address. */
566566+ kDNSServiceType_RT = 21, /* Router. */
567567+ kDNSServiceType_NSAP = 22, /* NSAP address. */
568568+ kDNSServiceType_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */
569569+ kDNSServiceType_SIG = 24, /* Security signature. */
570570+ kDNSServiceType_KEY = 25, /* Security key. */
571571+ kDNSServiceType_PX = 26, /* X.400 mail mapping. */
572572+ kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */
573573+ kDNSServiceType_AAAA = 28, /* IPv6 Address. */
574574+ kDNSServiceType_LOC = 29, /* Location Information. */
575575+ kDNSServiceType_NXT = 30, /* Next domain (security). */
576576+ kDNSServiceType_EID = 31, /* Endpoint identifier. */
577577+ kDNSServiceType_NIMLOC = 32, /* Nimrod Locator. */
578578+ kDNSServiceType_SRV = 33, /* Server Selection. */
579579+ kDNSServiceType_ATMA = 34, /* ATM Address */
580580+ kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */
581581+ kDNSServiceType_KX = 36, /* Key Exchange */
582582+ kDNSServiceType_CERT = 37, /* Certification record */
583583+ kDNSServiceType_A6 = 38, /* IPv6 Address (deprecated) */
584584+ kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */
585585+ kDNSServiceType_SINK = 40, /* Kitchen sink (experimental) */
586586+ kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */
587587+ kDNSServiceType_APL = 42, /* Address Prefix List */
588588+ kDNSServiceType_DS = 43, /* Delegation Signer */
589589+ kDNSServiceType_SSHFP = 44, /* SSH Key Fingerprint */
590590+ kDNSServiceType_IPSECKEY = 45, /* IPSECKEY */
591591+ kDNSServiceType_RRSIG = 46, /* RRSIG */
592592+ kDNSServiceType_NSEC = 47, /* Denial of Existence */
593593+ kDNSServiceType_DNSKEY = 48, /* DNSKEY */
594594+ kDNSServiceType_DHCID = 49, /* DHCP Client Identifier */
595595+ kDNSServiceType_NSEC3 = 50, /* Hashed Authenticated Denial of Existence */
596596+ kDNSServiceType_NSEC3PARAM = 51, /* Hashed Authenticated Denial of Existence */
597597+598598+ kDNSServiceType_HIP = 55, /* Host Identity Protocol */
599599+600600+ kDNSServiceType_SPF = 99, /* Sender Policy Framework for E-Mail */
601601+ kDNSServiceType_UINFO = 100, /* IANA-Reserved */
602602+ kDNSServiceType_UID = 101, /* IANA-Reserved */
603603+ kDNSServiceType_GID = 102, /* IANA-Reserved */
604604+ kDNSServiceType_UNSPEC = 103, /* IANA-Reserved */
605605+606606+ kDNSServiceType_TKEY = 249, /* Transaction key */
607607+ kDNSServiceType_TSIG = 250, /* Transaction signature. */
608608+ kDNSServiceType_IXFR = 251, /* Incremental zone transfer. */
609609+ kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */
610610+ kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */
611611+ kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */
612612+ kDNSServiceType_ANY = 255 /* Wildcard match. */
613613+};
614614+615615+/* possible error code values */
616616+enum
617617+{
618618+ kDNSServiceErr_NoError = 0,
619619+ kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */
620620+ kDNSServiceErr_NoSuchName = -65538,
621621+ kDNSServiceErr_NoMemory = -65539,
622622+ kDNSServiceErr_BadParam = -65540,
623623+ kDNSServiceErr_BadReference = -65541,
624624+ kDNSServiceErr_BadState = -65542,
625625+ kDNSServiceErr_BadFlags = -65543,
626626+ kDNSServiceErr_Unsupported = -65544,
627627+ kDNSServiceErr_NotInitialized = -65545,
628628+ kDNSServiceErr_AlreadyRegistered = -65547,
629629+ kDNSServiceErr_NameConflict = -65548,
630630+ kDNSServiceErr_Invalid = -65549,
631631+ kDNSServiceErr_Firewall = -65550,
632632+ kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */
633633+ kDNSServiceErr_BadInterfaceIndex = -65552,
634634+ kDNSServiceErr_Refused = -65553,
635635+ kDNSServiceErr_NoSuchRecord = -65554,
636636+ kDNSServiceErr_NoAuth = -65555,
637637+ kDNSServiceErr_NoSuchKey = -65556,
638638+ kDNSServiceErr_NATTraversal = -65557,
639639+ kDNSServiceErr_DoubleNAT = -65558,
640640+ kDNSServiceErr_BadTime = -65559, /* Codes up to here existed in Tiger */
641641+ kDNSServiceErr_BadSig = -65560,
642642+ kDNSServiceErr_BadKey = -65561,
643643+ kDNSServiceErr_Transient = -65562,
644644+ kDNSServiceErr_ServiceNotRunning = -65563, /* Background daemon not running */
645645+ kDNSServiceErr_NATPortMappingUnsupported = -65564, /* NAT doesn't support PCP, NAT-PMP or UPnP */
646646+ kDNSServiceErr_NATPortMappingDisabled = -65565, /* NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator */
647647+ kDNSServiceErr_NoRouter = -65566, /* No router currently configured (probably no network connectivity) */
648648+ kDNSServiceErr_PollingMode = -65567,
649649+ kDNSServiceErr_Timeout = -65568
650650+651651+ /* mDNS Error codes are in the range
652652+ * FFFE FF00 (-65792) to FFFE FFFF (-65537) */
653653+};
654654+655655+/* Maximum length, in bytes, of a service name represented as a */
656656+/* literal C-String, including the terminating NULL at the end. */
657657+658658+#define kDNSServiceMaxServiceName 64
659659+660660+/* Maximum length, in bytes, of a domain name represented as an *escaped* C-String */
661661+/* including the final trailing dot, and the C-String terminating NULL at the end. */
662662+663663+#define kDNSServiceMaxDomainName 1009
664664+665665+/*
666666+ * Notes on DNS Name Escaping
667667+ * -- or --
668668+ * "Why is kDNSServiceMaxDomainName 1009, when the maximum legal domain name is 256 bytes?"
669669+ *
670670+ * All strings used in the DNS-SD APIs are UTF-8 strings. Apart from the exceptions noted below,
671671+ * the APIs expect the strings to be properly escaped, using the conventional DNS escaping rules:
672672+ *
673673+ * '\\' represents a single literal '\' in the name
674674+ * '\.' represents a single literal '.' in the name
675675+ * '\ddd', where ddd is a three-digit decimal value from 000 to 255,
676676+ * represents a single literal byte with that value.
677677+ * A bare unescaped '.' is a label separator, marking a boundary between domain and subdomain.
678678+ *
679679+ * The exceptions, that do not use escaping, are the routines where the full
680680+ * DNS name of a resource is broken, for convenience, into servicename/regtype/domain.
681681+ * In these routines, the "servicename" is NOT escaped. It does not need to be, since
682682+ * it is, by definition, just a single literal string. Any characters in that string
683683+ * represent exactly what they are. The "regtype" portion is, technically speaking,
684684+ * escaped, but since legal regtypes are only allowed to contain letters, digits,
685685+ * and hyphens, there is nothing to escape, so the issue is moot. The "domain"
686686+ * portion is also escaped, though most domains in use on the public Internet
687687+ * today, like regtypes, don't contain any characters that need to be escaped.
688688+ * As DNS-SD becomes more popular, rich-text domains for service discovery will
689689+ * become common, so software should be written to cope with domains with escaping.
690690+ *
691691+ * The servicename may be up to 63 bytes of UTF-8 text (not counting the C-String
692692+ * terminating NULL at the end). The regtype is of the form _service._tcp or
693693+ * _service._udp, where the "service" part is 1-15 characters, which may be
694694+ * letters, digits, or hyphens. The domain part of the three-part name may be
695695+ * any legal domain, providing that the resulting servicename+regtype+domain
696696+ * name does not exceed 256 bytes.
697697+ *
698698+ * For most software, these issues are transparent. When browsing, the discovered
699699+ * servicenames should simply be displayed as-is. When resolving, the discovered
700700+ * servicename/regtype/domain are simply passed unchanged to DNSServiceResolve().
701701+ * When a DNSServiceResolve() succeeds, the returned fullname is already in
702702+ * the correct format to pass to standard system DNS APIs such as res_query().
703703+ * For converting from servicename/regtype/domain to a single properly-escaped
704704+ * full DNS name, the helper function DNSServiceConstructFullName() is provided.
705705+ *
706706+ * The following (highly contrived) example illustrates the escaping process.
707707+ * Suppose you have an service called "Dr. Smith\Dr. Johnson", of type "_ftp._tcp"
708708+ * in subdomain "4th. Floor" of subdomain "Building 2" of domain "apple.com."
709709+ * The full (escaped) DNS name of this service's SRV record would be:
710710+ * Dr\.\032Smith\\Dr\.\032Johnson._ftp._tcp.4th\.\032Floor.Building\0322.apple.com.
711711+ */
712712+713713+714714+/*
715715+ * Constants for specifying an interface index
716716+ *
717717+ * Specific interface indexes are identified via a 32-bit unsigned integer returned
718718+ * by the if_nametoindex() family of calls.
719719+ *
720720+ * If the client passes 0 for interface index, that means "do the right thing",
721721+ * which (at present) means, "if the name is in an mDNS local multicast domain
722722+ * (e.g. 'local.', '254.169.in-addr.arpa.', '{8,9,A,B}.E.F.ip6.arpa.') then multicast
723723+ * on all applicable interfaces, otherwise send via unicast to the appropriate
724724+ * DNS server." Normally, most clients will use 0 for interface index to
725725+ * automatically get the default sensible behaviour.
726726+ *
727727+ * If the client passes a positive interface index, then for multicast names that
728728+ * indicates to do the operation only on that one interface. For unicast names the
729729+ * interface index is ignored unless kDNSServiceFlagsForceMulticast is also set.
730730+ *
731731+ * If the client passes kDNSServiceInterfaceIndexLocalOnly when registering
732732+ * a service, then that service will be found *only* by other local clients
733733+ * on the same machine that are browsing using kDNSServiceInterfaceIndexLocalOnly
734734+ * or kDNSServiceInterfaceIndexAny.
735735+ * If a client has a 'private' service, accessible only to other processes
736736+ * running on the same machine, this allows the client to advertise that service
737737+ * in a way such that it does not inadvertently appear in service lists on
738738+ * all the other machines on the network.
739739+ *
740740+ * If the client passes kDNSServiceInterfaceIndexLocalOnly when browsing
741741+ * then it will find *all* records registered on that same local machine.
742742+ * Clients explicitly wishing to discover *only* LocalOnly services can
743743+ * accomplish this by inspecting the interfaceIndex of each service reported
744744+ * to their DNSServiceBrowseReply() callback function, and discarding those
745745+ * where the interface index is not kDNSServiceInterfaceIndexLocalOnly.
746746+ *
747747+ * kDNSServiceInterfaceIndexP2P is meaningful only in Browse, QueryRecord, Register,
748748+ * and Resolve operations. It should not be used in other DNSService APIs.
749749+ *
750750+ * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceBrowse or
751751+ * DNSServiceQueryRecord, it restricts the operation to P2P.
752752+ *
753753+ * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceRegister, it is
754754+ * mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P
755755+ * set.
756756+ *
757757+ * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceResolve, it is
758758+ * mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P
759759+ * set, because resolving a P2P service may create and/or enable an interface whose
760760+ * index is not known a priori. The resolve callback will indicate the index of the
761761+ * interface via which the service can be accessed.
762762+ *
763763+ * If applications pass kDNSServiceInterfaceIndexAny to DNSServiceBrowse
764764+ * or DNSServiceQueryRecord, they must set the kDNSServiceFlagsIncludeP2P flag
765765+ * to include P2P. In this case, if a service instance or the record being queried
766766+ * is found over P2P, the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P
767767+ * as the interface index.
768768+ */
769769+770770+#define kDNSServiceInterfaceIndexAny 0
771771+#define kDNSServiceInterfaceIndexLocalOnly ((uint32_t)-1)
772772+#define kDNSServiceInterfaceIndexUnicast ((uint32_t)-2)
773773+#define kDNSServiceInterfaceIndexP2P ((uint32_t)-3)
774774+775775+typedef uint32_t DNSServiceFlags;
776776+typedef uint32_t DNSServiceProtocol;
777777+typedef int32_t DNSServiceErrorType;
778778+779779+780780+/*********************************************************************************************
781781+*
782782+* Version checking
783783+*
784784+*********************************************************************************************/
785785+786786+/* DNSServiceGetProperty() Parameters:
787787+ *
788788+ * property: The requested property.
789789+ * Currently the only property defined is kDNSServiceProperty_DaemonVersion.
790790+ *
791791+ * result: Place to store result.
792792+ * For retrieving DaemonVersion, this should be the address of a uint32_t.
793793+ *
794794+ * size: Pointer to uint32_t containing size of the result location.
795795+ * For retrieving DaemonVersion, this should be sizeof(uint32_t).
796796+ * On return the uint32_t is updated to the size of the data returned.
797797+ * For DaemonVersion, the returned size is always sizeof(uint32_t), but
798798+ * future properties could be defined which return variable-sized results.
799799+ *
800800+ * return value: Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning
801801+ * if the daemon (or "system service" on Windows) is not running.
802802+ */
803803+804804+DNSServiceErrorType DNSSD_API DNSServiceGetProperty
805805+(
806806+ const char *property, /* Requested property (i.e. kDNSServiceProperty_DaemonVersion) */
807807+ void *result, /* Pointer to place to store result */
808808+ uint32_t *size /* size of result location */
809809+);
810810+811811+/*
812812+ * When requesting kDNSServiceProperty_DaemonVersion, the result pointer must point
813813+ * to a 32-bit unsigned integer, and the size parameter must be set to sizeof(uint32_t).
814814+ *
815815+ * On return, the 32-bit unsigned integer contains the API version number
816816+ *
817817+ * For example, Mac OS X 10.4.9 has API version 1080400.
818818+ * This allows applications to do simple greater-than and less-than comparisons:
819819+ * e.g. an application that requires at least API version 1080400 can check:
820820+ * if (version >= 1080400) ...
821821+ *
822822+ * Example usage:
823823+ * uint32_t version;
824824+ * uint32_t size = sizeof(version);
825825+ * DNSServiceErrorType err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &version, &size);
826826+ * if (!err) printf("DNS_SD API version is %d.%d\n", version / 10000, version / 100 % 100);
827827+ */
828828+829829+#define kDNSServiceProperty_DaemonVersion "DaemonVersion"
830830+831831+832832+// Map the source port of the local UDP socket that was opened for sending the DNS query
833833+// to the process ID of the application that triggered the DNS resolution.
834834+//
835835+/* DNSServiceGetPID() Parameters:
836836+ *
837837+ * srcport: Source port (in network byte order) of the UDP socket that was created by
838838+ * the daemon to send the DNS query on the wire.
839839+ *
840840+ * pid: Process ID of the application that started the name resolution which triggered
841841+ * the daemon to send the query on the wire. The value can be -1 if the srcport
842842+ * cannot be mapped.
843843+ *
844844+ * return value: Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning
845845+ * if the daemon is not running. The value of the pid is undefined if the return
846846+ * value has error.
847847+ */
848848+DNSServiceErrorType DNSSD_API DNSServiceGetPID
849849+(
850850+ uint16_t srcport,
851851+ int32_t *pid
852852+);
853853+854854+/*********************************************************************************************
855855+*
856856+* Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions
857857+*
858858+*********************************************************************************************/
859859+860860+/* DNSServiceRefSockFD()
861861+ *
862862+ * Access underlying Unix domain socket for an initialized DNSServiceRef.
863863+ * The DNS Service Discovery implementation uses this socket to communicate between the client and
864864+ * the daemon. The application MUST NOT directly read from or write to this socket.
865865+ * Access to the socket is provided so that it can be used as a kqueue event source, a CFRunLoop
866866+ * event source, in a select() loop, etc. When the underlying event management subsystem (kqueue/
867867+ * select/CFRunLoop etc.) indicates to the client that data is available for reading on the
868868+ * socket, the client should call DNSServiceProcessResult(), which will extract the daemon's
869869+ * reply from the socket, and pass it to the appropriate application callback. By using a run
870870+ * loop or select(), results from the daemon can be processed asynchronously. Alternatively,
871871+ * a client can choose to fork a thread and have it loop calling "DNSServiceProcessResult(ref);"
872872+ * If DNSServiceProcessResult() is called when no data is available for reading on the socket, it
873873+ * will block until data does become available, and then process the data and return to the caller.
874874+ * The application is reponsible for checking the return value of DNSServiceProcessResult() to determine
875875+ * if the socket is valid and if it should continue to process data on the socket.
876876+ * When data arrives on the socket, the client is responsible for calling DNSServiceProcessResult(ref)
877877+ * in a timely fashion -- if the client allows a large backlog of data to build up the daemon
878878+ * may terminate the connection.
879879+ *
880880+ * sdRef: A DNSServiceRef initialized by any of the DNSService calls.
881881+ *
882882+ * return value: The DNSServiceRef's underlying socket descriptor, or -1 on
883883+ * error.
884884+ */
885885+886886+int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef);
887887+888888+889889+/* DNSServiceProcessResult()
890890+ *
891891+ * Read a reply from the daemon, calling the appropriate application callback. This call will
892892+ * block until the daemon's response is received. Use DNSServiceRefSockFD() in
893893+ * conjunction with a run loop or select() to determine the presence of a response from the
894894+ * server before calling this function to process the reply without blocking. Call this function
895895+ * at any point if it is acceptable to block until the daemon's response arrives. Note that the
896896+ * client is responsible for ensuring that DNSServiceProcessResult() is called whenever there is
897897+ * a reply from the daemon - the daemon may terminate its connection with a client that does not
898898+ * process the daemon's responses.
899899+ *
900900+ * sdRef: A DNSServiceRef initialized by any of the DNSService calls
901901+ * that take a callback parameter.
902902+ *
903903+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns
904904+ * an error code indicating the specific failure that occurred.
905905+ */
906906+907907+DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef);
908908+909909+910910+/* DNSServiceRefDeallocate()
911911+ *
912912+ * Terminate a connection with the daemon and free memory associated with the DNSServiceRef.
913913+ * Any services or records registered with this DNSServiceRef will be deregistered. Any
914914+ * Browse, Resolve, or Query operations called with this reference will be terminated.
915915+ *
916916+ * Note: If the reference's underlying socket is used in a run loop or select() call, it should
917917+ * be removed BEFORE DNSServiceRefDeallocate() is called, as this function closes the reference's
918918+ * socket.
919919+ *
920920+ * Note: If the reference was initialized with DNSServiceCreateConnection(), any DNSRecordRefs
921921+ * created via this reference will be invalidated by this call - the resource records are
922922+ * deregistered, and their DNSRecordRefs may not be used in subsequent functions. Similarly,
923923+ * if the reference was initialized with DNSServiceRegister, and an extra resource record was
924924+ * added to the service via DNSServiceAddRecord(), the DNSRecordRef created by the Add() call
925925+ * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent
926926+ * functions.
927927+ *
928928+ * Note: This call is to be used only with the DNSServiceRef defined by this API.
929929+ *
930930+ * sdRef: A DNSServiceRef initialized by any of the DNSService calls.
931931+ *
932932+ */
933933+934934+void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef);
935935+936936+937937+/*********************************************************************************************
938938+*
939939+* Domain Enumeration
940940+*
941941+*********************************************************************************************/
942942+943943+/* DNSServiceEnumerateDomains()
944944+ *
945945+ * Asynchronously enumerate domains available for browsing and registration.
946946+ *
947947+ * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains
948948+ * are to be found.
949949+ *
950950+ * Note that the names returned are (like all of DNS-SD) UTF-8 strings,
951951+ * and are escaped using standard DNS escaping rules.
952952+ * (See "Notes on DNS Name Escaping" earlier in this file for more details.)
953953+ * A graphical browser displaying a hierarchical tree-structured view should cut
954954+ * the names at the bare dots to yield individual labels, then de-escape each
955955+ * label according to the escaping rules, and then display the resulting UTF-8 text.
956956+ *
957957+ * DNSServiceDomainEnumReply Callback Parameters:
958958+ *
959959+ * sdRef: The DNSServiceRef initialized by DNSServiceEnumerateDomains().
960960+ *
961961+ * flags: Possible values are:
962962+ * kDNSServiceFlagsMoreComing
963963+ * kDNSServiceFlagsAdd
964964+ * kDNSServiceFlagsDefault
965965+ *
966966+ * interfaceIndex: Specifies the interface on which the domain exists. (The index for a given
967967+ * interface is determined via the if_nametoindex() family of calls.)
968968+ *
969969+ * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise indicates
970970+ * the failure that occurred (other parameters are undefined if errorCode is nonzero).
971971+ *
972972+ * replyDomain: The name of the domain.
973973+ *
974974+ * context: The context pointer passed to DNSServiceEnumerateDomains.
975975+ *
976976+ */
977977+978978+typedef void (DNSSD_API *DNSServiceDomainEnumReply)
979979+(
980980+ DNSServiceRef sdRef,
981981+ DNSServiceFlags flags,
982982+ uint32_t interfaceIndex,
983983+ DNSServiceErrorType errorCode,
984984+ const char *replyDomain,
985985+ void *context
986986+);
987987+988988+989989+/* DNSServiceEnumerateDomains() Parameters:
990990+ *
991991+ * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds
992992+ * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
993993+ * and the enumeration operation will run indefinitely until the client
994994+ * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
995995+ *
996996+ * flags: Possible values are:
997997+ * kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing.
998998+ * kDNSServiceFlagsRegistrationDomains to enumerate domains recommended
999999+ * for registration.
10001000+ *
10011001+ * interfaceIndex: If non-zero, specifies the interface on which to look for domains.
10021002+ * (the index for a given interface is determined via the if_nametoindex()
10031003+ * family of calls.) Most applications will pass 0 to enumerate domains on
10041004+ * all interfaces. See "Constants for specifying an interface index" for more details.
10051005+ *
10061006+ * callBack: The function to be called when a domain is found or the call asynchronously
10071007+ * fails.
10081008+ *
10091009+ * context: An application context pointer which is passed to the callback function
10101010+ * (may be NULL).
10111011+ *
10121012+ * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
10131013+ * errors are delivered to the callback), otherwise returns an error code indicating
10141014+ * the error that occurred (the callback is not invoked and the DNSServiceRef
10151015+ * is not initialized).
10161016+ */
10171017+10181018+DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains
10191019+(
10201020+ DNSServiceRef *sdRef,
10211021+ DNSServiceFlags flags,
10221022+ uint32_t interfaceIndex,
10231023+ DNSServiceDomainEnumReply callBack,
10241024+ void *context /* may be NULL */
10251025+);
10261026+10271027+10281028+/*********************************************************************************************
10291029+*
10301030+* Service Registration
10311031+*
10321032+*********************************************************************************************/
10331033+10341034+/* Register a service that is discovered via Browse() and Resolve() calls.
10351035+ *
10361036+ * DNSServiceRegisterReply() Callback Parameters:
10371037+ *
10381038+ * sdRef: The DNSServiceRef initialized by DNSServiceRegister().
10391039+ *
10401040+ * flags: When a name is successfully registered, the callback will be
10411041+ * invoked with the kDNSServiceFlagsAdd flag set. When Wide-Area
10421042+ * DNS-SD is in use, it is possible for a single service to get
10431043+ * more than one success callback (e.g. one in the "local" multicast
10441044+ * DNS domain, and another in a wide-area unicast DNS domain).
10451045+ * If a successfully-registered name later suffers a name conflict
10461046+ * or similar problem and has to be deregistered, the callback will
10471047+ * be invoked with the kDNSServiceFlagsAdd flag not set. The callback
10481048+ * is *not* invoked in the case where the caller explicitly terminates
10491049+ * the service registration by calling DNSServiceRefDeallocate(ref);
10501050+ *
10511051+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
10521052+ * indicate the failure that occurred (including name conflicts,
10531053+ * if the kDNSServiceFlagsNoAutoRename flag was used when registering.)
10541054+ * Other parameters are undefined if errorCode is nonzero.
10551055+ *
10561056+ * name: The service name registered (if the application did not specify a name in
10571057+ * DNSServiceRegister(), this indicates what name was automatically chosen).
10581058+ *
10591059+ * regtype: The type of service registered, as it was passed to the callout.
10601060+ *
10611061+ * domain: The domain on which the service was registered (if the application did not
10621062+ * specify a domain in DNSServiceRegister(), this indicates the default domain
10631063+ * on which the service was registered).
10641064+ *
10651065+ * context: The context pointer that was passed to the callout.
10661066+ *
10671067+ */
10681068+10691069+typedef void (DNSSD_API *DNSServiceRegisterReply)
10701070+(
10711071+ DNSServiceRef sdRef,
10721072+ DNSServiceFlags flags,
10731073+ DNSServiceErrorType errorCode,
10741074+ const char *name,
10751075+ const char *regtype,
10761076+ const char *domain,
10771077+ void *context
10781078+);
10791079+10801080+10811081+/* DNSServiceRegister() Parameters:
10821082+ *
10831083+ * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds
10841084+ * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
10851085+ * and the registration will remain active indefinitely until the client
10861086+ * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
10871087+ *
10881088+ * interfaceIndex: If non-zero, specifies the interface on which to register the service
10891089+ * (the index for a given interface is determined via the if_nametoindex()
10901090+ * family of calls.) Most applications will pass 0 to register on all
10911091+ * available interfaces. See "Constants for specifying an interface index" for more details.
10921092+ *
10931093+ * flags: Indicates the renaming behavior on name conflict (most applications
10941094+ * will pass 0). See flag definitions above for details.
10951095+ *
10961096+ * name: If non-NULL, specifies the service name to be registered.
10971097+ * Most applications will not specify a name, in which case the computer
10981098+ * name is used (this name is communicated to the client via the callback).
10991099+ * If a name is specified, it must be 1-63 bytes of UTF-8 text.
11001100+ * If the name is longer than 63 bytes it will be automatically truncated
11011101+ * to a legal length, unless the NoAutoRename flag is set,
11021102+ * in which case kDNSServiceErr_BadParam will be returned.
11031103+ *
11041104+ * regtype: The service type followed by the protocol, separated by a dot
11051105+ * (e.g. "_ftp._tcp"). The service type must be an underscore, followed
11061106+ * by 1-15 characters, which may be letters, digits, or hyphens.
11071107+ * The transport protocol must be "_tcp" or "_udp". New service types
11081108+ * should be registered at <http://www.dns-sd.org/ServiceTypes.html>.
11091109+ *
11101110+ * Additional subtypes of the primary service type (where a service
11111111+ * type has defined subtypes) follow the primary service type in a
11121112+ * comma-separated list, with no additional spaces, e.g.
11131113+ * "_primarytype._tcp,_subtype1,_subtype2,_subtype3"
11141114+ * Subtypes provide a mechanism for filtered browsing: A client browsing
11151115+ * for "_primarytype._tcp" will discover all instances of this type;
11161116+ * a client browsing for "_primarytype._tcp,_subtype2" will discover only
11171117+ * those instances that were registered with "_subtype2" in their list of
11181118+ * registered subtypes.
11191119+ *
11201120+ * The subtype mechanism can be illustrated with some examples using the
11211121+ * dns-sd command-line tool:
11221122+ *
11231123+ * % dns-sd -R Simple _test._tcp "" 1001 &
11241124+ * % dns-sd -R Better _test._tcp,HasFeatureA "" 1002 &
11251125+ * % dns-sd -R Best _test._tcp,HasFeatureA,HasFeatureB "" 1003 &
11261126+ *
11271127+ * Now:
11281128+ * % dns-sd -B _test._tcp # will find all three services
11291129+ * % dns-sd -B _test._tcp,HasFeatureA # finds "Better" and "Best"
11301130+ * % dns-sd -B _test._tcp,HasFeatureB # finds only "Best"
11311131+ *
11321132+ * Subtype labels may be up to 63 bytes long, and may contain any eight-
11331133+ * bit byte values, including zero bytes. However, due to the nature of
11341134+ * using a C-string-based API, conventional DNS escaping must be used for
11351135+ * dots ('.'), commas (','), backslashes ('\') and zero bytes, as shown below:
11361136+ *
11371137+ * % dns-sd -R Test '_test._tcp,s\.one,s\,two,s\\three,s\000four' local 123
11381138+ *
11391139+ * When a service is registered, all the clients browsing for the registered
11401140+ * type ("regtype") will discover it. If the discovery should be
11411141+ * restricted to a smaller set of well known peers, the service can be
11421142+ * registered with additional data (group identifier) that is known
11431143+ * only to a smaller set of peers. The group identifier should follow primary
11441144+ * service type using a colon (":") as a delimeter. If subtypes are also present,
11451145+ * it should be given before the subtype as shown below.
11461146+ *
11471147+ * % dns-sd -R _test1 _http._tcp:mygroup1 local 1001
11481148+ * % dns-sd -R _test2 _http._tcp:mygroup2 local 1001
11491149+ * % dns-sd -R _test3 _http._tcp:mygroup3,HasFeatureA local 1001
11501150+ *
11511151+ * Now:
11521152+ * % dns-sd -B _http._tcp:"mygroup1" # will discover only test1
11531153+ * % dns-sd -B _http._tcp:"mygroup2" # will discover only test2
11541154+ * % dns-sd -B _http._tcp:"mygroup3",HasFeatureA # will discover only test3
11551155+ *
11561156+ * By specifying the group information, only the members of that group are
11571157+ * discovered.
11581158+ *
11591159+ * The group identifier itself is not sent in clear. Only a hash of the group
11601160+ * identifier is sent and the clients discover them anonymously. The group identifier
11611161+ * may be up to 256 bytes long and may contain any eight bit values except comma which
11621162+ * should be escaped.
11631163+ *
11641164+ * domain: If non-NULL, specifies the domain on which to advertise the service.
11651165+ * Most applications will not specify a domain, instead automatically
11661166+ * registering in the default domain(s).
11671167+ *
11681168+ * host: If non-NULL, specifies the SRV target host name. Most applications
11691169+ * will not specify a host, instead automatically using the machine's
11701170+ * default host name(s). Note that specifying a non-NULL host does NOT
11711171+ * create an address record for that host - the application is responsible
11721172+ * for ensuring that the appropriate address record exists, or creating it
11731173+ * via DNSServiceRegisterRecord().
11741174+ *
11751175+ * port: The port, in network byte order, on which the service accepts connections.
11761176+ * Pass 0 for a "placeholder" service (i.e. a service that will not be discovered
11771177+ * by browsing, but will cause a name conflict if another client tries to
11781178+ * register that same name). Most clients will not use placeholder services.
11791179+ *
11801180+ * txtLen: The length of the txtRecord, in bytes. Must be zero if the txtRecord is NULL.
11811181+ *
11821182+ * txtRecord: The TXT record rdata. A non-NULL txtRecord MUST be a properly formatted DNS
11831183+ * TXT record, i.e. <length byte> <data> <length byte> <data> ...
11841184+ * Passing NULL for the txtRecord is allowed as a synonym for txtLen=1, txtRecord="",
11851185+ * i.e. it creates a TXT record of length one containing a single empty string.
11861186+ * RFC 1035 doesn't allow a TXT record to contain *zero* strings, so a single empty
11871187+ * string is the smallest legal DNS TXT record.
11881188+ * As with the other parameters, the DNSServiceRegister call copies the txtRecord
11891189+ * data; e.g. if you allocated the storage for the txtRecord parameter with malloc()
11901190+ * then you can safely free that memory right after the DNSServiceRegister call returns.
11911191+ *
11921192+ * callBack: The function to be called when the registration completes or asynchronously
11931193+ * fails. The client MAY pass NULL for the callback - The client will NOT be notified
11941194+ * of the default values picked on its behalf, and the client will NOT be notified of any
11951195+ * asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration
11961196+ * of the service. The client may NOT pass the NoAutoRename flag if the callback is NULL.
11971197+ * The client may still deregister the service at any time via DNSServiceRefDeallocate().
11981198+ *
11991199+ * context: An application context pointer which is passed to the callback function
12001200+ * (may be NULL).
12011201+ *
12021202+ * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
12031203+ * errors are delivered to the callback), otherwise returns an error code indicating
12041204+ * the error that occurred (the callback is never invoked and the DNSServiceRef
12051205+ * is not initialized).
12061206+ */
12071207+12081208+DNSServiceErrorType DNSSD_API DNSServiceRegister
12091209+(
12101210+ DNSServiceRef *sdRef,
12111211+ DNSServiceFlags flags,
12121212+ uint32_t interfaceIndex,
12131213+ const char *name, /* may be NULL */
12141214+ const char *regtype,
12151215+ const char *domain, /* may be NULL */
12161216+ const char *host, /* may be NULL */
12171217+ uint16_t port, /* In network byte order */
12181218+ uint16_t txtLen,
12191219+ const void *txtRecord, /* may be NULL */
12201220+ DNSServiceRegisterReply callBack, /* may be NULL */
12211221+ void *context /* may be NULL */
12221222+);
12231223+12241224+12251225+/* DNSServiceAddRecord()
12261226+ *
12271227+ * Add a record to a registered service. The name of the record will be the same as the
12281228+ * registered service's name.
12291229+ * The record can later be updated or deregistered by passing the RecordRef initialized
12301230+ * by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
12311231+ *
12321232+ * Note that the DNSServiceAddRecord/UpdateRecord/RemoveRecord are *NOT* thread-safe
12331233+ * with respect to a single DNSServiceRef. If you plan to have multiple threads
12341234+ * in your program simultaneously add, update, or remove records from the same
12351235+ * DNSServiceRef, then it's the caller's responsibility to use a mutext lock
12361236+ * or take similar appropriate precautions to serialize those calls.
12371237+ *
12381238+ * Parameters;
12391239+ *
12401240+ * sdRef: A DNSServiceRef initialized by DNSServiceRegister().
12411241+ *
12421242+ * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this
12431243+ * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
12441244+ * If the above DNSServiceRef is passed to DNSServiceRefDeallocate(), RecordRef is also
12451245+ * invalidated and may not be used further.
12461246+ *
12471247+ * flags: Currently ignored, reserved for future use.
12481248+ *
12491249+ * rrtype: The type of the record (e.g. kDNSServiceType_TXT, kDNSServiceType_SRV, etc)
12501250+ *
12511251+ * rdlen: The length, in bytes, of the rdata.
12521252+ *
12531253+ * rdata: The raw rdata to be contained in the added resource record.
12541254+ *
12551255+ * ttl: The time to live of the resource record, in seconds.
12561256+ * Most clients should pass 0 to indicate that the system should
12571257+ * select a sensible default value.
12581258+ *
12591259+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an
12601260+ * error code indicating the error that occurred (the RecordRef is not initialized).
12611261+ */
12621262+12631263+DNSServiceErrorType DNSSD_API DNSServiceAddRecord
12641264+(
12651265+ DNSServiceRef sdRef,
12661266+ DNSRecordRef *RecordRef,
12671267+ DNSServiceFlags flags,
12681268+ uint16_t rrtype,
12691269+ uint16_t rdlen,
12701270+ const void *rdata,
12711271+ uint32_t ttl
12721272+);
12731273+12741274+12751275+/* DNSServiceUpdateRecord
12761276+ *
12771277+ * Update a registered resource record. The record must either be:
12781278+ * - The primary txt record of a service registered via DNSServiceRegister()
12791279+ * - A record added to a registered service via DNSServiceAddRecord()
12801280+ * - An individual record registered by DNSServiceRegisterRecord()
12811281+ *
12821282+ * Parameters:
12831283+ *
12841284+ * sdRef: A DNSServiceRef that was initialized by DNSServiceRegister()
12851285+ * or DNSServiceCreateConnection().
12861286+ *
12871287+ * RecordRef: A DNSRecordRef initialized by DNSServiceAddRecord, or NULL to update the
12881288+ * service's primary txt record.
12891289+ *
12901290+ * flags: Currently ignored, reserved for future use.
12911291+ *
12921292+ * rdlen: The length, in bytes, of the new rdata.
12931293+ *
12941294+ * rdata: The new rdata to be contained in the updated resource record.
12951295+ *
12961296+ * ttl: The time to live of the updated resource record, in seconds.
12971297+ * Most clients should pass 0 to indicate that the system should
12981298+ * select a sensible default value.
12991299+ *
13001300+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an
13011301+ * error code indicating the error that occurred.
13021302+ */
13031303+13041304+DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord
13051305+(
13061306+ DNSServiceRef sdRef,
13071307+ DNSRecordRef RecordRef, /* may be NULL */
13081308+ DNSServiceFlags flags,
13091309+ uint16_t rdlen,
13101310+ const void *rdata,
13111311+ uint32_t ttl
13121312+);
13131313+13141314+13151315+/* DNSServiceRemoveRecord
13161316+ *
13171317+ * Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister
13181318+ * an record registered individually via DNSServiceRegisterRecord().
13191319+ *
13201320+ * Parameters:
13211321+ *
13221322+ * sdRef: A DNSServiceRef initialized by DNSServiceRegister() (if the
13231323+ * record being removed was registered via DNSServiceAddRecord()) or by
13241324+ * DNSServiceCreateConnection() (if the record being removed was registered via
13251325+ * DNSServiceRegisterRecord()).
13261326+ *
13271327+ * recordRef: A DNSRecordRef initialized by a successful call to DNSServiceAddRecord()
13281328+ * or DNSServiceRegisterRecord().
13291329+ *
13301330+ * flags: Currently ignored, reserved for future use.
13311331+ *
13321332+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an
13331333+ * error code indicating the error that occurred.
13341334+ */
13351335+13361336+DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord
13371337+(
13381338+ DNSServiceRef sdRef,
13391339+ DNSRecordRef RecordRef,
13401340+ DNSServiceFlags flags
13411341+);
13421342+13431343+13441344+/*********************************************************************************************
13451345+*
13461346+* Service Discovery
13471347+*
13481348+*********************************************************************************************/
13491349+13501350+/* Browse for instances of a service.
13511351+ *
13521352+ * DNSServiceBrowseReply() Parameters:
13531353+ *
13541354+ * sdRef: The DNSServiceRef initialized by DNSServiceBrowse().
13551355+ *
13561356+ * flags: Possible values are kDNSServiceFlagsMoreComing and kDNSServiceFlagsAdd.
13571357+ * See flag definitions for details.
13581358+ *
13591359+ * interfaceIndex: The interface on which the service is advertised. This index should
13601360+ * be passed to DNSServiceResolve() when resolving the service.
13611361+ *
13621362+ * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will
13631363+ * indicate the failure that occurred. Other parameters are undefined if
13641364+ * the errorCode is nonzero.
13651365+ *
13661366+ * serviceName: The discovered service name. This name should be displayed to the user,
13671367+ * and stored for subsequent use in the DNSServiceResolve() call.
13681368+ *
13691369+ * regtype: The service type, which is usually (but not always) the same as was passed
13701370+ * to DNSServiceBrowse(). One case where the discovered service type may
13711371+ * not be the same as the requested service type is when using subtypes:
13721372+ * The client may want to browse for only those ftp servers that allow
13731373+ * anonymous connections. The client will pass the string "_ftp._tcp,_anon"
13741374+ * to DNSServiceBrowse(), but the type of the service that's discovered
13751375+ * is simply "_ftp._tcp". The regtype for each discovered service instance
13761376+ * should be stored along with the name, so that it can be passed to
13771377+ * DNSServiceResolve() when the service is later resolved.
13781378+ *
13791379+ * domain: The domain of the discovered service instance. This may or may not be the
13801380+ * same as the domain that was passed to DNSServiceBrowse(). The domain for each
13811381+ * discovered service instance should be stored along with the name, so that
13821382+ * it can be passed to DNSServiceResolve() when the service is later resolved.
13831383+ *
13841384+ * context: The context pointer that was passed to the callout.
13851385+ *
13861386+ */
13871387+13881388+typedef void (DNSSD_API *DNSServiceBrowseReply)
13891389+(
13901390+ DNSServiceRef sdRef,
13911391+ DNSServiceFlags flags,
13921392+ uint32_t interfaceIndex,
13931393+ DNSServiceErrorType errorCode,
13941394+ const char *serviceName,
13951395+ const char *regtype,
13961396+ const char *replyDomain,
13971397+ void *context
13981398+);
13991399+14001400+14011401+/* DNSServiceBrowse() Parameters:
14021402+ *
14031403+ * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds
14041404+ * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
14051405+ * and the browse operation will run indefinitely until the client
14061406+ * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
14071407+ *
14081408+ * flags: Currently ignored, reserved for future use.
14091409+ *
14101410+ * interfaceIndex: If non-zero, specifies the interface on which to browse for services
14111411+ * (the index for a given interface is determined via the if_nametoindex()
14121412+ * family of calls.) Most applications will pass 0 to browse on all available
14131413+ * interfaces. See "Constants for specifying an interface index" for more details.
14141414+ *
14151415+ * regtype: The service type being browsed for followed by the protocol, separated by a
14161416+ * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
14171417+ * A client may optionally specify a single subtype to perform filtered browsing:
14181418+ * e.g. browsing for "_primarytype._tcp,_subtype" will discover only those
14191419+ * instances of "_primarytype._tcp" that were registered specifying "_subtype"
14201420+ * in their list of registered subtypes. Additionally, a group identifier may
14211421+ * also be specified before the subtype e.g., _primarytype._tcp:GroupID, which
14221422+ * will discover only the members that register the service with GroupID. See
14231423+ * DNSServiceRegister for more details.
14241424+ *
14251425+ * domain: If non-NULL, specifies the domain on which to browse for services.
14261426+ * Most applications will not specify a domain, instead browsing on the
14271427+ * default domain(s).
14281428+ *
14291429+ * callBack: The function to be called when an instance of the service being browsed for
14301430+ * is found, or if the call asynchronously fails.
14311431+ *
14321432+ * context: An application context pointer which is passed to the callback function
14331433+ * (may be NULL).
14341434+ *
14351435+ * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
14361436+ * errors are delivered to the callback), otherwise returns an error code indicating
14371437+ * the error that occurred (the callback is not invoked and the DNSServiceRef
14381438+ * is not initialized).
14391439+ */
14401440+14411441+DNSServiceErrorType DNSSD_API DNSServiceBrowse
14421442+(
14431443+ DNSServiceRef *sdRef,
14441444+ DNSServiceFlags flags,
14451445+ uint32_t interfaceIndex,
14461446+ const char *regtype,
14471447+ const char *domain, /* may be NULL */
14481448+ DNSServiceBrowseReply callBack,
14491449+ void *context /* may be NULL */
14501450+);
14511451+14521452+14531453+/* DNSServiceResolve()
14541454+ *
14551455+ * Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and
14561456+ * txt record.
14571457+ *
14581458+ * Note: Applications should NOT use DNSServiceResolve() solely for txt record monitoring - use
14591459+ * DNSServiceQueryRecord() instead, as it is more efficient for this task.
14601460+ *
14611461+ * Note: When the desired results have been returned, the client MUST terminate the resolve by calling
14621462+ * DNSServiceRefDeallocate().
14631463+ *
14641464+ * Note: DNSServiceResolve() behaves correctly for typical services that have a single SRV record
14651465+ * and a single TXT record. To resolve non-standard services with multiple SRV or TXT records,
14661466+ * DNSServiceQueryRecord() should be used.
14671467+ *
14681468+ * DNSServiceResolveReply Callback Parameters:
14691469+ *
14701470+ * sdRef: The DNSServiceRef initialized by DNSServiceResolve().
14711471+ *
14721472+ * flags: Possible values: kDNSServiceFlagsMoreComing
14731473+ *
14741474+ * interfaceIndex: The interface on which the service was resolved.
14751475+ *
14761476+ * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will
14771477+ * indicate the failure that occurred. Other parameters are undefined if
14781478+ * the errorCode is nonzero.
14791479+ *
14801480+ * fullname: The full service domain name, in the form <servicename>.<protocol>.<domain>.
14811481+ * (This name is escaped following standard DNS rules, making it suitable for
14821482+ * passing to standard system DNS APIs such as res_query(), or to the
14831483+ * special-purpose functions included in this API that take fullname parameters.
14841484+ * See "Notes on DNS Name Escaping" earlier in this file for more details.)
14851485+ *
14861486+ * hosttarget: The target hostname of the machine providing the service. This name can
14871487+ * be passed to functions like gethostbyname() to identify the host's IP address.
14881488+ *
14891489+ * port: The port, in network byte order, on which connections are accepted for this service.
14901490+ *
14911491+ * txtLen: The length of the txt record, in bytes.
14921492+ *
14931493+ * txtRecord: The service's primary txt record, in standard txt record format.
14941494+ *
14951495+ * context: The context pointer that was passed to the callout.
14961496+ *
14971497+ * NOTE: In earlier versions of this header file, the txtRecord parameter was declared "const char *"
14981498+ * This is incorrect, since it contains length bytes which are values in the range 0 to 255, not -128 to +127.
14991499+ * Depending on your compiler settings, this change may cause signed/unsigned mismatch warnings.
15001500+ * These should be fixed by updating your own callback function definition to match the corrected
15011501+ * function signature using "const unsigned char *txtRecord". Making this change may also fix inadvertent
15021502+ * bugs in your callback function, where it could have incorrectly interpreted a length byte with value 250
15031503+ * as being -6 instead, with various bad consequences ranging from incorrect operation to software crashes.
15041504+ * If you need to maintain portable code that will compile cleanly with both the old and new versions of
15051505+ * this header file, you should update your callback function definition to use the correct unsigned value,
15061506+ * and then in the place where you pass your callback function to DNSServiceResolve(), use a cast to eliminate
15071507+ * the compiler warning, e.g.:
15081508+ * DNSServiceResolve(sd, flags, index, name, regtype, domain, (DNSServiceResolveReply)MyCallback, context);
15091509+ * This will ensure that your code compiles cleanly without warnings (and more importantly, works correctly)
15101510+ * with both the old header and with the new corrected version.
15111511+ *
15121512+ */
15131513+15141514+typedef void (DNSSD_API *DNSServiceResolveReply)
15151515+(
15161516+ DNSServiceRef sdRef,
15171517+ DNSServiceFlags flags,
15181518+ uint32_t interfaceIndex,
15191519+ DNSServiceErrorType errorCode,
15201520+ const char *fullname,
15211521+ const char *hosttarget,
15221522+ uint16_t port, /* In network byte order */
15231523+ uint16_t txtLen,
15241524+ const unsigned char *txtRecord,
15251525+ void *context
15261526+);
15271527+15281528+15291529+/* DNSServiceResolve() Parameters
15301530+ *
15311531+ * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds
15321532+ * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
15331533+ * and the resolve operation will run indefinitely until the client
15341534+ * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
15351535+ *
15361536+ * flags: Specifying kDNSServiceFlagsForceMulticast will cause query to be
15371537+ * performed with a link-local mDNS query, even if the name is an
15381538+ * apparently non-local name (i.e. a name not ending in ".local.")
15391539+ *
15401540+ * interfaceIndex: The interface on which to resolve the service. If this resolve call is
15411541+ * as a result of a currently active DNSServiceBrowse() operation, then the
15421542+ * interfaceIndex should be the index reported in the DNSServiceBrowseReply
15431543+ * callback. If this resolve call is using information previously saved
15441544+ * (e.g. in a preference file) for later use, then use interfaceIndex 0, because
15451545+ * the desired service may now be reachable via a different physical interface.
15461546+ * See "Constants for specifying an interface index" for more details.
15471547+ *
15481548+ * name: The name of the service instance to be resolved, as reported to the
15491549+ * DNSServiceBrowseReply() callback.
15501550+ *
15511551+ * regtype: The type of the service instance to be resolved, as reported to the
15521552+ * DNSServiceBrowseReply() callback.
15531553+ *
15541554+ * domain: The domain of the service instance to be resolved, as reported to the
15551555+ * DNSServiceBrowseReply() callback.
15561556+ *
15571557+ * callBack: The function to be called when a result is found, or if the call
15581558+ * asynchronously fails.
15591559+ *
15601560+ * context: An application context pointer which is passed to the callback function
15611561+ * (may be NULL).
15621562+ *
15631563+ * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
15641564+ * errors are delivered to the callback), otherwise returns an error code indicating
15651565+ * the error that occurred (the callback is never invoked and the DNSServiceRef
15661566+ * is not initialized).
15671567+ */
15681568+15691569+DNSServiceErrorType DNSSD_API DNSServiceResolve
15701570+(
15711571+ DNSServiceRef *sdRef,
15721572+ DNSServiceFlags flags,
15731573+ uint32_t interfaceIndex,
15741574+ const char *name,
15751575+ const char *regtype,
15761576+ const char *domain,
15771577+ DNSServiceResolveReply callBack,
15781578+ void *context /* may be NULL */
15791579+);
15801580+15811581+15821582+/*********************************************************************************************
15831583+*
15841584+* Querying Individual Specific Records
15851585+*
15861586+*********************************************************************************************/
15871587+15881588+/* DNSServiceQueryRecord
15891589+ *
15901590+ * Query for an arbitrary DNS record.
15911591+ *
15921592+ * DNSServiceQueryRecordReply() Callback Parameters:
15931593+ *
15941594+ * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord().
15951595+ *
15961596+ * flags: Possible values are kDNSServiceFlagsMoreComing and
15971597+ * kDNSServiceFlagsAdd. The Add flag is NOT set for PTR records
15981598+ * with a ttl of 0, i.e. "Remove" events.
15991599+ *
16001600+ * interfaceIndex: The interface on which the query was resolved (the index for a given
16011601+ * interface is determined via the if_nametoindex() family of calls).
16021602+ * See "Constants for specifying an interface index" for more details.
16031603+ *
16041604+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
16051605+ * indicate the failure that occurred. Other parameters are undefined if
16061606+ * errorCode is nonzero.
16071607+ *
16081608+ * fullname: The resource record's full domain name.
16091609+ *
16101610+ * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
16111611+ *
16121612+ * rrclass: The class of the resource record (usually kDNSServiceClass_IN).
16131613+ *
16141614+ * rdlen: The length, in bytes, of the resource record rdata.
16151615+ *
16161616+ * rdata: The raw rdata of the resource record.
16171617+ *
16181618+ * ttl: If the client wishes to cache the result for performance reasons,
16191619+ * the TTL indicates how long the client may legitimately hold onto
16201620+ * this result, in seconds. After the TTL expires, the client should
16211621+ * consider the result no longer valid, and if it requires this data
16221622+ * again, it should be re-fetched with a new query. Of course, this
16231623+ * only applies to clients that cancel the asynchronous operation when
16241624+ * they get a result. Clients that leave the asynchronous operation
16251625+ * running can safely assume that the data remains valid until they
16261626+ * get another callback telling them otherwise.
16271627+ *
16281628+ * context: The context pointer that was passed to the callout.
16291629+ *
16301630+ */
16311631+16321632+typedef void (DNSSD_API *DNSServiceQueryRecordReply)
16331633+(
16341634+ DNSServiceRef sdRef,
16351635+ DNSServiceFlags flags,
16361636+ uint32_t interfaceIndex,
16371637+ DNSServiceErrorType errorCode,
16381638+ const char *fullname,
16391639+ uint16_t rrtype,
16401640+ uint16_t rrclass,
16411641+ uint16_t rdlen,
16421642+ const void *rdata,
16431643+ uint32_t ttl,
16441644+ void *context
16451645+);
16461646+16471647+16481648+/* DNSServiceQueryRecord() Parameters:
16491649+ *
16501650+ * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds
16511651+ * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
16521652+ * and the query operation will run indefinitely until the client
16531653+ * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
16541654+ *
16551655+ * flags: kDNSServiceFlagsForceMulticast or kDNSServiceFlagsLongLivedQuery.
16561656+ * Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast
16571657+ * query to a unicast DNS server that implements the protocol. This flag
16581658+ * has no effect on link-local multicast queries.
16591659+ *
16601660+ * interfaceIndex: If non-zero, specifies the interface on which to issue the query
16611661+ * (the index for a given interface is determined via the if_nametoindex()
16621662+ * family of calls.) Passing 0 causes the name to be queried for on all
16631663+ * interfaces. See "Constants for specifying an interface index" for more details.
16641664+ *
16651665+ * fullname: The full domain name of the resource record to be queried for.
16661666+ *
16671667+ * rrtype: The numerical type of the resource record to be queried for
16681668+ * (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
16691669+ *
16701670+ * rrclass: The class of the resource record (usually kDNSServiceClass_IN).
16711671+ *
16721672+ * callBack: The function to be called when a result is found, or if the call
16731673+ * asynchronously fails.
16741674+ *
16751675+ * context: An application context pointer which is passed to the callback function
16761676+ * (may be NULL).
16771677+ *
16781678+ * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
16791679+ * errors are delivered to the callback), otherwise returns an error code indicating
16801680+ * the error that occurred (the callback is never invoked and the DNSServiceRef
16811681+ * is not initialized).
16821682+ */
16831683+16841684+DNSServiceErrorType DNSSD_API DNSServiceQueryRecord
16851685+(
16861686+ DNSServiceRef *sdRef,
16871687+ DNSServiceFlags flags,
16881688+ uint32_t interfaceIndex,
16891689+ const char *fullname,
16901690+ uint16_t rrtype,
16911691+ uint16_t rrclass,
16921692+ DNSServiceQueryRecordReply callBack,
16931693+ void *context /* may be NULL */
16941694+);
16951695+16961696+16971697+/*********************************************************************************************
16981698+*
16991699+* Unified lookup of both IPv4 and IPv6 addresses for a fully qualified hostname
17001700+*
17011701+*********************************************************************************************/
17021702+17031703+/* DNSServiceGetAddrInfo
17041704+ *
17051705+ * Queries for the IP address of a hostname by using either Multicast or Unicast DNS.
17061706+ *
17071707+ * DNSServiceGetAddrInfoReply() parameters:
17081708+ *
17091709+ * sdRef: The DNSServiceRef initialized by DNSServiceGetAddrInfo().
17101710+ *
17111711+ * flags: Possible values are kDNSServiceFlagsMoreComing and
17121712+ * kDNSServiceFlagsAdd.
17131713+ *
17141714+ * interfaceIndex: The interface to which the answers pertain.
17151715+ *
17161716+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
17171717+ * indicate the failure that occurred. Other parameters are
17181718+ * undefined if errorCode is nonzero.
17191719+ *
17201720+ * hostname: The fully qualified domain name of the host to be queried for.
17211721+ *
17221722+ * address: IPv4 or IPv6 address.
17231723+ *
17241724+ * ttl: If the client wishes to cache the result for performance reasons,
17251725+ * the TTL indicates how long the client may legitimately hold onto
17261726+ * this result, in seconds. After the TTL expires, the client should
17271727+ * consider the result no longer valid, and if it requires this data
17281728+ * again, it should be re-fetched with a new query. Of course, this
17291729+ * only applies to clients that cancel the asynchronous operation when
17301730+ * they get a result. Clients that leave the asynchronous operation
17311731+ * running can safely assume that the data remains valid until they
17321732+ * get another callback telling them otherwise.
17331733+ *
17341734+ * context: The context pointer that was passed to the callout.
17351735+ *
17361736+ */
17371737+17381738+typedef void (DNSSD_API *DNSServiceGetAddrInfoReply)
17391739+(
17401740+ DNSServiceRef sdRef,
17411741+ DNSServiceFlags flags,
17421742+ uint32_t interfaceIndex,
17431743+ DNSServiceErrorType errorCode,
17441744+ const char *hostname,
17451745+ const struct sockaddr *address,
17461746+ uint32_t ttl,
17471747+ void *context
17481748+);
17491749+17501750+17511751+/* DNSServiceGetAddrInfo() Parameters:
17521752+ *
17531753+ * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds then it
17541754+ * initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the query
17551755+ * begins and will last indefinitely until the client terminates the query
17561756+ * by passing this DNSServiceRef to DNSServiceRefDeallocate().
17571757+ *
17581758+ * flags: kDNSServiceFlagsForceMulticast
17591759+ *
17601760+ * interfaceIndex: The interface on which to issue the query. Passing 0 causes the query to be
17611761+ * sent on all active interfaces via Multicast or the primary interface via Unicast.
17621762+ *
17631763+ * protocol: Pass in kDNSServiceProtocol_IPv4 to look up IPv4 addresses, or kDNSServiceProtocol_IPv6
17641764+ * to look up IPv6 addresses, or both to look up both kinds. If neither flag is
17651765+ * set, the system will apply an intelligent heuristic, which is (currently)
17661766+ * that it will attempt to look up both, except:
17671767+ *
17681768+ * * If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name)
17691769+ * but this host has no routable IPv6 address, then the call will not try to
17701770+ * look up IPv6 addresses for "hostname", since any addresses it found would be
17711771+ * unlikely to be of any use anyway. Similarly, if this host has no routable
17721772+ * IPv4 address, the call will not try to look up IPv4 addresses for "hostname".
17731773+ *
17741774+ * hostname: The fully qualified domain name of the host to be queried for.
17751775+ *
17761776+ * callBack: The function to be called when the query succeeds or fails asynchronously.
17771777+ *
17781778+ * context: An application context pointer which is passed to the callback function
17791779+ * (may be NULL).
17801780+ *
17811781+ * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
17821782+ * errors are delivered to the callback), otherwise returns an error code indicating
17831783+ * the error that occurred.
17841784+ */
17851785+17861786+DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo
17871787+(
17881788+ DNSServiceRef *sdRef,
17891789+ DNSServiceFlags flags,
17901790+ uint32_t interfaceIndex,
17911791+ DNSServiceProtocol protocol,
17921792+ const char *hostname,
17931793+ DNSServiceGetAddrInfoReply callBack,
17941794+ void *context /* may be NULL */
17951795+);
17961796+17971797+17981798+/*********************************************************************************************
17991799+*
18001800+* Special Purpose Calls:
18011801+* DNSServiceCreateConnection(), DNSServiceRegisterRecord(), DNSServiceReconfirmRecord()
18021802+* (most applications will not use these)
18031803+*
18041804+*********************************************************************************************/
18051805+18061806+/* DNSServiceCreateConnection()
18071807+ *
18081808+ * Create a connection to the daemon allowing efficient registration of
18091809+ * multiple individual records.
18101810+ *
18111811+ * Parameters:
18121812+ *
18131813+ * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating
18141814+ * the reference (via DNSServiceRefDeallocate()) severs the
18151815+ * connection and deregisters all records registered on this connection.
18161816+ *
18171817+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns
18181818+ * an error code indicating the specific failure that occurred (in which
18191819+ * case the DNSServiceRef is not initialized).
18201820+ */
18211821+18221822+DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef);
18231823+18241824+/* DNSServiceRegisterRecord
18251825+ *
18261826+ * Register an individual resource record on a connected DNSServiceRef.
18271827+ *
18281828+ * Note that name conflicts occurring for records registered via this call must be handled
18291829+ * by the client in the callback.
18301830+ *
18311831+ * DNSServiceRegisterRecordReply() parameters:
18321832+ *
18331833+ * sdRef: The connected DNSServiceRef initialized by
18341834+ * DNSServiceCreateConnection().
18351835+ *
18361836+ * RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord(). If the above
18371837+ * DNSServiceRef is passed to DNSServiceRefDeallocate(), this DNSRecordRef is
18381838+ * invalidated, and may not be used further.
18391839+ *
18401840+ * flags: Currently unused, reserved for future use.
18411841+ *
18421842+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
18431843+ * indicate the failure that occurred (including name conflicts.)
18441844+ * Other parameters are undefined if errorCode is nonzero.
18451845+ *
18461846+ * context: The context pointer that was passed to the callout.
18471847+ *
18481848+ */
18491849+18501850+typedef void (DNSSD_API *DNSServiceRegisterRecordReply)
18511851+(
18521852+ DNSServiceRef sdRef,
18531853+ DNSRecordRef RecordRef,
18541854+ DNSServiceFlags flags,
18551855+ DNSServiceErrorType errorCode,
18561856+ void *context
18571857+);
18581858+18591859+18601860+/* DNSServiceRegisterRecord() Parameters:
18611861+ *
18621862+ * sdRef: A DNSServiceRef initialized by DNSServiceCreateConnection().
18631863+ *
18641864+ * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this
18651865+ * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
18661866+ * (To deregister ALL records registered on a single connected DNSServiceRef
18671867+ * and deallocate each of their corresponding DNSServiceRecordRefs, call
18681868+ * DNSServiceRefDeallocate()).
18691869+ *
18701870+ * flags: Possible values are kDNSServiceFlagsShared or kDNSServiceFlagsUnique
18711871+ * (see flag type definitions for details).
18721872+ *
18731873+ * interfaceIndex: If non-zero, specifies the interface on which to register the record
18741874+ * (the index for a given interface is determined via the if_nametoindex()
18751875+ * family of calls.) Passing 0 causes the record to be registered on all interfaces.
18761876+ * See "Constants for specifying an interface index" for more details.
18771877+ *
18781878+ * fullname: The full domain name of the resource record.
18791879+ *
18801880+ * rrtype: The numerical type of the resource record (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
18811881+ *
18821882+ * rrclass: The class of the resource record (usually kDNSServiceClass_IN)
18831883+ *
18841884+ * rdlen: Length, in bytes, of the rdata.
18851885+ *
18861886+ * rdata: A pointer to the raw rdata, as it is to appear in the DNS record.
18871887+ *
18881888+ * ttl: The time to live of the resource record, in seconds.
18891889+ * Most clients should pass 0 to indicate that the system should
18901890+ * select a sensible default value.
18911891+ *
18921892+ * callBack: The function to be called when a result is found, or if the call
18931893+ * asynchronously fails (e.g. because of a name conflict.)
18941894+ *
18951895+ * context: An application context pointer which is passed to the callback function
18961896+ * (may be NULL).
18971897+ *
18981898+ * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
18991899+ * errors are delivered to the callback), otherwise returns an error code indicating
19001900+ * the error that occurred (the callback is never invoked and the DNSRecordRef is
19011901+ * not initialized).
19021902+ */
19031903+19041904+DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord
19051905+(
19061906+ DNSServiceRef sdRef,
19071907+ DNSRecordRef *RecordRef,
19081908+ DNSServiceFlags flags,
19091909+ uint32_t interfaceIndex,
19101910+ const char *fullname,
19111911+ uint16_t rrtype,
19121912+ uint16_t rrclass,
19131913+ uint16_t rdlen,
19141914+ const void *rdata,
19151915+ uint32_t ttl,
19161916+ DNSServiceRegisterRecordReply callBack,
19171917+ void *context /* may be NULL */
19181918+);
19191919+19201920+19211921+/* DNSServiceReconfirmRecord
19221922+ *
19231923+ * Instruct the daemon to verify the validity of a resource record that appears
19241924+ * to be out of date (e.g. because TCP connection to a service's target failed.)
19251925+ * Causes the record to be flushed from the daemon's cache (as well as all other
19261926+ * daemons' caches on the network) if the record is determined to be invalid.
19271927+ * Use this routine conservatively. Reconfirming a record necessarily consumes
19281928+ * network bandwidth, so this should not be done indiscriminately.
19291929+ *
19301930+ * Parameters:
19311931+ *
19321932+ * flags: Not currently used.
19331933+ *
19341934+ * interfaceIndex: Specifies the interface of the record in question.
19351935+ * The caller must specify the interface.
19361936+ * This API (by design) causes increased network traffic, so it requires
19371937+ * the caller to be precise about which record should be reconfirmed.
19381938+ * It is not possible to pass zero for the interface index to perform
19391939+ * a "wildcard" reconfirmation, where *all* matching records are reconfirmed.
19401940+ *
19411941+ * fullname: The resource record's full domain name.
19421942+ *
19431943+ * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
19441944+ *
19451945+ * rrclass: The class of the resource record (usually kDNSServiceClass_IN).
19461946+ *
19471947+ * rdlen: The length, in bytes, of the resource record rdata.
19481948+ *
19491949+ * rdata: The raw rdata of the resource record.
19501950+ *
19511951+ */
19521952+19531953+DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord
19541954+(
19551955+ DNSServiceFlags flags,
19561956+ uint32_t interfaceIndex,
19571957+ const char *fullname,
19581958+ uint16_t rrtype,
19591959+ uint16_t rrclass,
19601960+ uint16_t rdlen,
19611961+ const void *rdata
19621962+);
19631963+19641964+#ifndef __OPEN_SOURCE__
19651965+19661966+/* PeerConnectionRelease() Parameters
19671967+ *
19681968+ * Release P2P connection resources associated with the service instance.
19691969+ * When a service is resolved over a P2P interface, a connection is brought up to the
19701970+ * peer advertising the service instance. This call will free the resources associated
19711971+ * with that connection. Note that the reference to the service instance will only
19721972+ * be maintained by the daemon while the browse for the service type is still
19731973+ * running. Thus the sequence of calls to discover, resolve, and then terminate the connection
19741974+ * associated with a given P2P service instance would be:
19751975+ *
19761976+ * DNSServiceRef BrowseRef, ResolveRef;
19771977+ * DNSServiceBrowse(&BrowseRef, ...) // browse for all instances of the service
19781978+ * DNSServiceResolve(&ResolveRef, ...) // resolving a service instance creates a
19791979+ * // connection to the peer device advertising that service
19801980+ * DNSServiceRefDeallocate(ResolveRef) // Stop the resolve, which does not close the peer connection
19811981+ *
19821982+ * // Communicate with the peer application.
19831983+ *
19841984+ * PeerConnectionRelease() // release the connection to the peer device for the specified service instance
19851985+ *
19861986+ * DNSServiceRefDeallocate(BrowseRef) // stop the browse
19871987+ * // Any further calls to PeerConnectionRelease() will have no affect since the
19881988+ * // service instance to peer connection relationship is only maintained by the
19891989+ * // daemon while the browse is running.
19901990+ *
19911991+ *
19921992+ * flags: Not currently used.
19931993+ *
19941994+ * name: The name of the service instance to be resolved, as reported to the
19951995+ * DNSServiceBrowseReply() callback.
19961996+ *
19971997+ * regtype: The type of the service instance to be resolved, as reported to the
19981998+ * DNSServiceBrowseReply() callback.
19991999+ *
20002000+ * domain: The domain of the service instance to be resolved, as reported to the
20012001+ * DNSServiceBrowseReply() callback.
20022002+ *
20032003+ * return value: Returns kDNSServiceErr_NoError on success or the error that occurred.
20042004+ */
20052005+20062006+DNSServiceErrorType DNSSD_API PeerConnectionRelease
20072007+(
20082008+ DNSServiceFlags flags,
20092009+ const char *name,
20102010+ const char *regtype,
20112011+ const char *domain
20122012+);
20132013+20142014+#endif // __OPEN_SOURCE__
20152015+20162016+/*********************************************************************************************
20172017+*
20182018+* NAT Port Mapping
20192019+*
20202020+*********************************************************************************************/
20212021+20222022+/* DNSServiceNATPortMappingCreate
20232023+ *
20242024+ * Request a port mapping in the NAT gateway, which maps a port on the local machine
20252025+ * to an external port on the NAT. The NAT should support either PCP, NAT-PMP or the
20262026+ * UPnP/IGD protocol for this API to create a successful mapping. Note that this API
20272027+ * currently supports IPv4 addresses/mappings only. If the NAT gateway supports PCP and
20282028+ * returns an IPv6 address (incorrectly, since this API specifically requests IPv4
20292029+ * addresses), the DNSServiceNATPortMappingReply callback will be invoked with errorCode
20302030+ * kDNSServiceErr_NATPortMappingUnsupported.
20312031+ *
20322032+ * The port mapping will be renewed indefinitely until the client process exits, or
20332033+ * explicitly terminates the port mapping request by calling DNSServiceRefDeallocate().
20342034+ * The client callback will be invoked, informing the client of the NAT gateway's
20352035+ * external IP address and the external port that has been allocated for this client.
20362036+ * The client should then record this external IP address and port using whatever
20372037+ * directory service mechanism it is using to enable peers to connect to it.
20382038+ * (Clients advertising services using Wide-Area DNS-SD DO NOT need to use this API
20392039+ * -- when a client calls DNSServiceRegister() NAT mappings are automatically created
20402040+ * and the external IP address and port for the service are recorded in the global DNS.
20412041+ * Only clients using some directory mechanism other than Wide-Area DNS-SD need to use
20422042+ * this API to explicitly map their own ports.)
20432043+ *
20442044+ * It's possible that the client callback could be called multiple times, for example
20452045+ * if the NAT gateway's IP address changes, or if a configuration change results in a
20462046+ * different external port being mapped for this client. Over the lifetime of any long-lived
20472047+ * port mapping, the client should be prepared to handle these notifications of changes
20482048+ * in the environment, and should update its recorded address and/or port as appropriate.
20492049+ *
20502050+ * NOTE: There are two unusual aspects of how the DNSServiceNATPortMappingCreate API works,
20512051+ * which were intentionally designed to help simplify client code:
20522052+ *
20532053+ * 1. It's not an error to request a NAT mapping when the machine is not behind a NAT gateway.
20542054+ * In other NAT mapping APIs, if you request a NAT mapping and the machine is not behind a NAT
20552055+ * gateway, then the API returns an error code -- it can't get you a NAT mapping if there's no
20562056+ * NAT gateway. The DNSServiceNATPortMappingCreate API takes a different view. Working out
20572057+ * whether or not you need a NAT mapping can be tricky and non-obvious, particularly on
20582058+ * a machine with multiple active network interfaces. Rather than make every client recreate
20592059+ * this logic for deciding whether a NAT mapping is required, the PortMapping API does that
20602060+ * work for you. If the client calls the PortMapping API when the machine already has a
20612061+ * routable public IP address, then instead of complaining about it and giving an error,
20622062+ * the PortMapping API just invokes your callback, giving the machine's public address
20632063+ * and your own port number. This means you don't need to write code to work out whether
20642064+ * your client needs to call the PortMapping API -- just call it anyway, and if it wasn't
20652065+ * necessary, no harm is done:
20662066+ *
20672067+ * - If the machine already has a routable public IP address, then your callback
20682068+ * will just be invoked giving your own address and port.
20692069+ * - If a NAT mapping is required and obtained, then your callback will be invoked
20702070+ * giving you the external address and port.
20712071+ * - If a NAT mapping is required but not obtained from the local NAT gateway,
20722072+ * or the machine has no network connectivity, then your callback will be
20732073+ * invoked giving zero address and port.
20742074+ *
20752075+ * 2. In other NAT mapping APIs, if a laptop computer is put to sleep and woken up on a new
20762076+ * network, it's the client's job to notice this, and work out whether a NAT mapping
20772077+ * is required on the new network, and make a new NAT mapping request if necessary.
20782078+ * The DNSServiceNATPortMappingCreate API does this for you, automatically.
20792079+ * The client just needs to make one call to the PortMapping API, and its callback will
20802080+ * be invoked any time the mapping state changes. This property complements point (1) above.
20812081+ * If the client didn't make a NAT mapping request just because it determined that one was
20822082+ * not required at that particular moment in time, the client would then have to monitor
20832083+ * for network state changes to determine if a NAT port mapping later became necessary.
20842084+ * By unconditionally making a NAT mapping request, even when a NAT mapping not to be
20852085+ * necessary, the PortMapping API will then begin monitoring network state changes on behalf of
20862086+ * the client, and if a NAT mapping later becomes necessary, it will automatically create a NAT
20872087+ * mapping and inform the client with a new callback giving the new address and port information.
20882088+ *
20892089+ * DNSServiceNATPortMappingReply() parameters:
20902090+ *
20912091+ * sdRef: The DNSServiceRef initialized by DNSServiceNATPortMappingCreate().
20922092+ *
20932093+ * flags: Currently unused, reserved for future use.
20942094+ *
20952095+ * interfaceIndex: The interface through which the NAT gateway is reached.
20962096+ *
20972097+ * errorCode: Will be kDNSServiceErr_NoError on success.
20982098+ * Will be kDNSServiceErr_DoubleNAT when the NAT gateway is itself behind one or
20992099+ * more layers of NAT, in which case the other parameters have the defined values.
21002100+ * For other failures, will indicate the failure that occurred, and the other
21012101+ * parameters are undefined.
21022102+ *
21032103+ * externalAddress: Four byte IPv4 address in network byte order.
21042104+ *
21052105+ * protocol: Will be kDNSServiceProtocol_UDP or kDNSServiceProtocol_TCP or both.
21062106+ *
21072107+ * internalPort: The port on the local machine that was mapped.
21082108+ *
21092109+ * externalPort: The actual external port in the NAT gateway that was mapped.
21102110+ * This is likely to be different than the requested external port.
21112111+ *
21122112+ * ttl: The lifetime of the NAT port mapping created on the gateway.
21132113+ * This controls how quickly stale mappings will be garbage-collected
21142114+ * if the client machine crashes, suffers a power failure, is disconnected
21152115+ * from the network, or suffers some other unfortunate demise which
21162116+ * causes it to vanish without explicitly removing its NAT port mapping.
21172117+ * It's possible that the ttl value will differ from the requested ttl value.
21182118+ *
21192119+ * context: The context pointer that was passed to the callout.
21202120+ *
21212121+ */
21222122+21232123+typedef void (DNSSD_API *DNSServiceNATPortMappingReply)
21242124+(
21252125+ DNSServiceRef sdRef,
21262126+ DNSServiceFlags flags,
21272127+ uint32_t interfaceIndex,
21282128+ DNSServiceErrorType errorCode,
21292129+ uint32_t externalAddress, /* four byte IPv4 address in network byte order */
21302130+ DNSServiceProtocol protocol,
21312131+ uint16_t internalPort, /* In network byte order */
21322132+ uint16_t externalPort, /* In network byte order and may be different than the requested port */
21332133+ uint32_t ttl, /* may be different than the requested ttl */
21342134+ void *context
21352135+);
21362136+21372137+21382138+/* DNSServiceNATPortMappingCreate() Parameters:
21392139+ *
21402140+ * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds then it
21412141+ * initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the nat
21422142+ * port mapping will last indefinitely until the client terminates the port
21432143+ * mapping request by passing this DNSServiceRef to DNSServiceRefDeallocate().
21442144+ *
21452145+ * flags: Currently ignored, reserved for future use.
21462146+ *
21472147+ * interfaceIndex: The interface on which to create port mappings in a NAT gateway. Passing 0 causes
21482148+ * the port mapping request to be sent on the primary interface.
21492149+ *
21502150+ * protocol: To request a port mapping, pass in kDNSServiceProtocol_UDP, or kDNSServiceProtocol_TCP,
21512151+ * or (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP) to map both.
21522152+ * The local listening port number must also be specified in the internalPort parameter.
21532153+ * To just discover the NAT gateway's external IP address, pass zero for protocol,
21542154+ * internalPort, externalPort and ttl.
21552155+ *
21562156+ * internalPort: The port number in network byte order on the local machine which is listening for packets.
21572157+ *
21582158+ * externalPort: The requested external port in network byte order in the NAT gateway that you would
21592159+ * like to map to the internal port. Pass 0 if you don't care which external port is chosen for you.
21602160+ *
21612161+ * ttl: The requested renewal period of the NAT port mapping, in seconds.
21622162+ * If the client machine crashes, suffers a power failure, is disconnected from
21632163+ * the network, or suffers some other unfortunate demise which causes it to vanish
21642164+ * unexpectedly without explicitly removing its NAT port mappings, then the NAT gateway
21652165+ * will garbage-collect old stale NAT port mappings when their lifetime expires.
21662166+ * Requesting a short TTL causes such orphaned mappings to be garbage-collected
21672167+ * more promptly, but consumes system resources and network bandwidth with
21682168+ * frequent renewal packets to keep the mapping from expiring.
21692169+ * Requesting a long TTL is more efficient on the network, but in the event of the
21702170+ * client vanishing, stale NAT port mappings will not be garbage-collected as quickly.
21712171+ * Most clients should pass 0 to use a system-wide default value.
21722172+ *
21732173+ * callBack: The function to be called when the port mapping request succeeds or fails asynchronously.
21742174+ *
21752175+ * context: An application context pointer which is passed to the callback function
21762176+ * (may be NULL).
21772177+ *
21782178+ * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
21792179+ * errors are delivered to the callback), otherwise returns an error code indicating
21802180+ * the error that occurred.
21812181+ *
21822182+ * If you don't actually want a port mapped, and are just calling the API
21832183+ * because you want to find out the NAT's external IP address (e.g. for UI
21842184+ * display) then pass zero for protocol, internalPort, externalPort and ttl.
21852185+ */
21862186+21872187+DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate
21882188+(
21892189+ DNSServiceRef *sdRef,
21902190+ DNSServiceFlags flags,
21912191+ uint32_t interfaceIndex,
21922192+ DNSServiceProtocol protocol, /* TCP and/or UDP */
21932193+ uint16_t internalPort, /* network byte order */
21942194+ uint16_t externalPort, /* network byte order */
21952195+ uint32_t ttl, /* time to live in seconds */
21962196+ DNSServiceNATPortMappingReply callBack,
21972197+ void *context /* may be NULL */
21982198+);
21992199+22002200+22012201+/*********************************************************************************************
22022202+*
22032203+* General Utility Functions
22042204+*
22052205+*********************************************************************************************/
22062206+22072207+/* DNSServiceConstructFullName()
22082208+ *
22092209+ * Concatenate a three-part domain name (as returned by the above callbacks) into a
22102210+ * properly-escaped full domain name. Note that callbacks in the above functions ALREADY ESCAPE
22112211+ * strings where necessary.
22122212+ *
22132213+ * Parameters:
22142214+ *
22152215+ * fullName: A pointer to a buffer that where the resulting full domain name is to be written.
22162216+ * The buffer must be kDNSServiceMaxDomainName (1009) bytes in length to
22172217+ * accommodate the longest legal domain name without buffer overrun.
22182218+ *
22192219+ * service: The service name - any dots or backslashes must NOT be escaped.
22202220+ * May be NULL (to construct a PTR record name, e.g.
22212221+ * "_ftp._tcp.apple.com.").
22222222+ *
22232223+ * regtype: The service type followed by the protocol, separated by a dot
22242224+ * (e.g. "_ftp._tcp").
22252225+ *
22262226+ * domain: The domain name, e.g. "apple.com.". Literal dots or backslashes,
22272227+ * if any, must be escaped, e.g. "1st\. Floor.apple.com."
22282228+ *
22292229+ * return value: Returns kDNSServiceErr_NoError (0) on success, kDNSServiceErr_BadParam on error.
22302230+ *
22312231+ */
22322232+22332233+DNSServiceErrorType DNSSD_API DNSServiceConstructFullName
22342234+(
22352235+ char * const fullName,
22362236+ const char * const service, /* may be NULL */
22372237+ const char * const regtype,
22382238+ const char * const domain
22392239+);
22402240+22412241+22422242+/*********************************************************************************************
22432243+*
22442244+* TXT Record Construction Functions
22452245+*
22462246+*********************************************************************************************/
22472247+22482248+/*
22492249+ * A typical calling sequence for TXT record construction is something like:
22502250+ *
22512251+ * Client allocates storage for TXTRecord data (e.g. declare buffer on the stack)
22522252+ * TXTRecordCreate();
22532253+ * TXTRecordSetValue();
22542254+ * TXTRecordSetValue();
22552255+ * TXTRecordSetValue();
22562256+ * ...
22572257+ * DNSServiceRegister( ... TXTRecordGetLength(), TXTRecordGetBytesPtr() ... );
22582258+ * TXTRecordDeallocate();
22592259+ * Explicitly deallocate storage for TXTRecord data (if not allocated on the stack)
22602260+ */
22612261+22622262+22632263+/* TXTRecordRef
22642264+ *
22652265+ * Opaque internal data type.
22662266+ * Note: Represents a DNS-SD TXT record.
22672267+ */
22682268+22692269+typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef;
22702270+22712271+22722272+/* TXTRecordCreate()
22732273+ *
22742274+ * Creates a new empty TXTRecordRef referencing the specified storage.
22752275+ *
22762276+ * If the buffer parameter is NULL, or the specified storage size is not
22772277+ * large enough to hold a key subsequently added using TXTRecordSetValue(),
22782278+ * then additional memory will be added as needed using malloc().
22792279+ *
22802280+ * On some platforms, when memory is low, malloc() may fail. In this
22812281+ * case, TXTRecordSetValue() will return kDNSServiceErr_NoMemory, and this
22822282+ * error condition will need to be handled as appropriate by the caller.
22832283+ *
22842284+ * You can avoid the need to handle this error condition if you ensure
22852285+ * that the storage you initially provide is large enough to hold all
22862286+ * the key/value pairs that are to be added to the record.
22872287+ * The caller can precompute the exact length required for all of the
22882288+ * key/value pairs to be added, or simply provide a fixed-sized buffer
22892289+ * known in advance to be large enough.
22902290+ * A no-value (key-only) key requires (1 + key length) bytes.
22912291+ * A key with empty value requires (1 + key length + 1) bytes.
22922292+ * A key with non-empty value requires (1 + key length + 1 + value length).
22932293+ * For most applications, DNS-SD TXT records are generally
22942294+ * less than 100 bytes, so in most cases a simple fixed-sized
22952295+ * 256-byte buffer will be more than sufficient.
22962296+ * Recommended size limits for DNS-SD TXT Records are discussed in
22972297+ * <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt>
22982298+ *
22992299+ * Note: When passing parameters to and from these TXT record APIs,
23002300+ * the key name does not include the '=' character. The '=' character
23012301+ * is the separator between the key and value in the on-the-wire
23022302+ * packet format; it is not part of either the key or the value.
23032303+ *
23042304+ * txtRecord: A pointer to an uninitialized TXTRecordRef.
23052305+ *
23062306+ * bufferLen: The size of the storage provided in the "buffer" parameter.
23072307+ *
23082308+ * buffer: Optional caller-supplied storage used to hold the TXTRecord data.
23092309+ * This storage must remain valid for as long as
23102310+ * the TXTRecordRef.
23112311+ */
23122312+23132313+void DNSSD_API TXTRecordCreate
23142314+(
23152315+ TXTRecordRef *txtRecord,
23162316+ uint16_t bufferLen,
23172317+ void *buffer
23182318+);
23192319+23202320+23212321+/* TXTRecordDeallocate()
23222322+ *
23232323+ * Releases any resources allocated in the course of preparing a TXT Record
23242324+ * using TXTRecordCreate()/TXTRecordSetValue()/TXTRecordRemoveValue().
23252325+ * Ownership of the buffer provided in TXTRecordCreate() returns to the client.
23262326+ *
23272327+ * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate().
23282328+ *
23292329+ */
23302330+23312331+void DNSSD_API TXTRecordDeallocate
23322332+(
23332333+ TXTRecordRef *txtRecord
23342334+);
23352335+23362336+23372337+/* TXTRecordSetValue()
23382338+ *
23392339+ * Adds a key (optionally with value) to a TXTRecordRef. If the "key" already
23402340+ * exists in the TXTRecordRef, then the current value will be replaced with
23412341+ * the new value.
23422342+ * Keys may exist in four states with respect to a given TXT record:
23432343+ * - Absent (key does not appear at all)
23442344+ * - Present with no value ("key" appears alone)
23452345+ * - Present with empty value ("key=" appears in TXT record)
23462346+ * - Present with non-empty value ("key=value" appears in TXT record)
23472347+ * For more details refer to "Data Syntax for DNS-SD TXT Records" in
23482348+ * <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt>
23492349+ *
23502350+ * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate().
23512351+ *
23522352+ * key: A null-terminated string which only contains printable ASCII
23532353+ * values (0x20-0x7E), excluding '=' (0x3D). Keys should be
23542354+ * 9 characters or fewer (not counting the terminating null).
23552355+ *
23562356+ * valueSize: The size of the value.
23572357+ *
23582358+ * value: Any binary value. For values that represent
23592359+ * textual data, UTF-8 is STRONGLY recommended.
23602360+ * For values that represent textual data, valueSize
23612361+ * should NOT include the terminating null (if any)
23622362+ * at the end of the string.
23632363+ * If NULL, then "key" will be added with no value.
23642364+ * If non-NULL but valueSize is zero, then "key=" will be
23652365+ * added with empty value.
23662366+ *
23672367+ * return value: Returns kDNSServiceErr_NoError on success.
23682368+ * Returns kDNSServiceErr_Invalid if the "key" string contains
23692369+ * illegal characters.
23702370+ * Returns kDNSServiceErr_NoMemory if adding this key would
23712371+ * exceed the available storage.
23722372+ */
23732373+23742374+DNSServiceErrorType DNSSD_API TXTRecordSetValue
23752375+(
23762376+ TXTRecordRef *txtRecord,
23772377+ const char *key,
23782378+ uint8_t valueSize, /* may be zero */
23792379+ const void *value /* may be NULL */
23802380+);
23812381+23822382+23832383+/* TXTRecordRemoveValue()
23842384+ *
23852385+ * Removes a key from a TXTRecordRef. The "key" must be an
23862386+ * ASCII string which exists in the TXTRecordRef.
23872387+ *
23882388+ * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate().
23892389+ *
23902390+ * key: A key name which exists in the TXTRecordRef.
23912391+ *
23922392+ * return value: Returns kDNSServiceErr_NoError on success.
23932393+ * Returns kDNSServiceErr_NoSuchKey if the "key" does not
23942394+ * exist in the TXTRecordRef.
23952395+ */
23962396+23972397+DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
23982398+(
23992399+ TXTRecordRef *txtRecord,
24002400+ const char *key
24012401+);
24022402+24032403+24042404+/* TXTRecordGetLength()
24052405+ *
24062406+ * Allows you to determine the length of the raw bytes within a TXTRecordRef.
24072407+ *
24082408+ * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate().
24092409+ *
24102410+ * return value: Returns the size of the raw bytes inside a TXTRecordRef
24112411+ * which you can pass directly to DNSServiceRegister() or
24122412+ * to DNSServiceUpdateRecord().
24132413+ * Returns 0 if the TXTRecordRef is empty.
24142414+ */
24152415+24162416+uint16_t DNSSD_API TXTRecordGetLength
24172417+(
24182418+ const TXTRecordRef *txtRecord
24192419+);
24202420+24212421+24222422+/* TXTRecordGetBytesPtr()
24232423+ *
24242424+ * Allows you to retrieve a pointer to the raw bytes within a TXTRecordRef.
24252425+ *
24262426+ * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate().
24272427+ *
24282428+ * return value: Returns a pointer to the raw bytes inside the TXTRecordRef
24292429+ * which you can pass directly to DNSServiceRegister() or
24302430+ * to DNSServiceUpdateRecord().
24312431+ */
24322432+24332433+const void * DNSSD_API TXTRecordGetBytesPtr
24342434+(
24352435+ const TXTRecordRef *txtRecord
24362436+);
24372437+24382438+24392439+/*********************************************************************************************
24402440+*
24412441+* TXT Record Parsing Functions
24422442+*
24432443+*********************************************************************************************/
24442444+24452445+/*
24462446+ * A typical calling sequence for TXT record parsing is something like:
24472447+ *
24482448+ * Receive TXT record data in DNSServiceResolve() callback
24492449+ * if (TXTRecordContainsKey(txtLen, txtRecord, "key")) then do something
24502450+ * val1ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key1", &len1);
24512451+ * val2ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key2", &len2);
24522452+ * ...
24532453+ * memcpy(myval1, val1ptr, len1);
24542454+ * memcpy(myval2, val2ptr, len2);
24552455+ * ...
24562456+ * return;
24572457+ *
24582458+ * If you wish to retain the values after return from the DNSServiceResolve()
24592459+ * callback, then you need to copy the data to your own storage using memcpy()
24602460+ * or similar, as shown in the example above.
24612461+ *
24622462+ * If for some reason you need to parse a TXT record you built yourself
24632463+ * using the TXT record construction functions above, then you can do
24642464+ * that using TXTRecordGetLength and TXTRecordGetBytesPtr calls:
24652465+ * TXTRecordGetValue(TXTRecordGetLength(x), TXTRecordGetBytesPtr(x), key, &len);
24662466+ *
24672467+ * Most applications only fetch keys they know about from a TXT record and
24682468+ * ignore the rest.
24692469+ * However, some debugging tools wish to fetch and display all keys.
24702470+ * To do that, use the TXTRecordGetCount() and TXTRecordGetItemAtIndex() calls.
24712471+ */
24722472+24732473+/* TXTRecordContainsKey()
24742474+ *
24752475+ * Allows you to determine if a given TXT Record contains a specified key.
24762476+ *
24772477+ * txtLen: The size of the received TXT Record.
24782478+ *
24792479+ * txtRecord: Pointer to the received TXT Record bytes.
24802480+ *
24812481+ * key: A null-terminated ASCII string containing the key name.
24822482+ *
24832483+ * return value: Returns 1 if the TXT Record contains the specified key.
24842484+ * Otherwise, it returns 0.
24852485+ */
24862486+24872487+int DNSSD_API TXTRecordContainsKey
24882488+(
24892489+ uint16_t txtLen,
24902490+ const void *txtRecord,
24912491+ const char *key
24922492+);
24932493+24942494+24952495+/* TXTRecordGetValuePtr()
24962496+ *
24972497+ * Allows you to retrieve the value for a given key from a TXT Record.
24982498+ *
24992499+ * txtLen: The size of the received TXT Record
25002500+ *
25012501+ * txtRecord: Pointer to the received TXT Record bytes.
25022502+ *
25032503+ * key: A null-terminated ASCII string containing the key name.
25042504+ *
25052505+ * valueLen: On output, will be set to the size of the "value" data.
25062506+ *
25072507+ * return value: Returns NULL if the key does not exist in this TXT record,
25082508+ * or exists with no value (to differentiate between
25092509+ * these two cases use TXTRecordContainsKey()).
25102510+ * Returns pointer to location within TXT Record bytes
25112511+ * if the key exists with empty or non-empty value.
25122512+ * For empty value, valueLen will be zero.
25132513+ * For non-empty value, valueLen will be length of value data.
25142514+ */
25152515+25162516+const void * DNSSD_API TXTRecordGetValuePtr
25172517+(
25182518+ uint16_t txtLen,
25192519+ const void *txtRecord,
25202520+ const char *key,
25212521+ uint8_t *valueLen
25222522+);
25232523+25242524+25252525+/* TXTRecordGetCount()
25262526+ *
25272527+ * Returns the number of keys stored in the TXT Record. The count
25282528+ * can be used with TXTRecordGetItemAtIndex() to iterate through the keys.
25292529+ *
25302530+ * txtLen: The size of the received TXT Record.
25312531+ *
25322532+ * txtRecord: Pointer to the received TXT Record bytes.
25332533+ *
25342534+ * return value: Returns the total number of keys in the TXT Record.
25352535+ *
25362536+ */
25372537+25382538+uint16_t DNSSD_API TXTRecordGetCount
25392539+(
25402540+ uint16_t txtLen,
25412541+ const void *txtRecord
25422542+);
25432543+25442544+25452545+/* TXTRecordGetItemAtIndex()
25462546+ *
25472547+ * Allows you to retrieve a key name and value pointer, given an index into
25482548+ * a TXT Record. Legal index values range from zero to TXTRecordGetCount()-1.
25492549+ * It's also possible to iterate through keys in a TXT record by simply
25502550+ * calling TXTRecordGetItemAtIndex() repeatedly, beginning with index zero
25512551+ * and increasing until TXTRecordGetItemAtIndex() returns kDNSServiceErr_Invalid.
25522552+ *
25532553+ * On return:
25542554+ * For keys with no value, *value is set to NULL and *valueLen is zero.
25552555+ * For keys with empty value, *value is non-NULL and *valueLen is zero.
25562556+ * For keys with non-empty value, *value is non-NULL and *valueLen is non-zero.
25572557+ *
25582558+ * txtLen: The size of the received TXT Record.
25592559+ *
25602560+ * txtRecord: Pointer to the received TXT Record bytes.
25612561+ *
25622562+ * itemIndex: An index into the TXT Record.
25632563+ *
25642564+ * keyBufLen: The size of the string buffer being supplied.
25652565+ *
25662566+ * key: A string buffer used to store the key name.
25672567+ * On return, the buffer contains a null-terminated C string
25682568+ * giving the key name. DNS-SD TXT keys are usually
25692569+ * 9 characters or fewer. To hold the maximum possible
25702570+ * key name, the buffer should be 256 bytes long.
25712571+ *
25722572+ * valueLen: On output, will be set to the size of the "value" data.
25732573+ *
25742574+ * value: On output, *value is set to point to location within TXT
25752575+ * Record bytes that holds the value data.
25762576+ *
25772577+ * return value: Returns kDNSServiceErr_NoError on success.
25782578+ * Returns kDNSServiceErr_NoMemory if keyBufLen is too short.
25792579+ * Returns kDNSServiceErr_Invalid if index is greater than
25802580+ * TXTRecordGetCount()-1.
25812581+ */
25822582+25832583+DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
25842584+(
25852585+ uint16_t txtLen,
25862586+ const void *txtRecord,
25872587+ uint16_t itemIndex,
25882588+ uint16_t keyBufLen,
25892589+ char *key,
25902590+ uint8_t *valueLen,
25912591+ const void **value
25922592+);
25932593+25942594+#if _DNS_SD_LIBDISPATCH
25952595+/*
25962596+ * DNSServiceSetDispatchQueue
25972597+ *
25982598+ * Allows you to schedule a DNSServiceRef on a serial dispatch queue for receiving asynchronous
25992599+ * callbacks. It's the clients responsibility to ensure that the provided dispatch queue is running.
26002600+ *
26012601+ * A typical application that uses CFRunLoopRun or dispatch_main on its main thread will
26022602+ * usually schedule DNSServiceRefs on its main queue (which is always a serial queue)
26032603+ * using "DNSServiceSetDispatchQueue(sdref, dispatch_get_main_queue());"
26042604+ *
26052605+ * If there is any error during the processing of events, the application callback will
26062606+ * be called with an error code. For shared connections, each subordinate DNSServiceRef
26072607+ * will get its own error callback. Currently these error callbacks only happen
26082608+ * if the daemon is manually terminated or crashes, and the error
26092609+ * code in this case is kDNSServiceErr_ServiceNotRunning. The application must call
26102610+ * DNSServiceRefDeallocate to free the DNSServiceRef when it gets such an error code.
26112611+ * These error callbacks are rare and should not normally happen on customer machines,
26122612+ * but application code should be written defensively to handle such error callbacks
26132613+ * gracefully if they occur.
26142614+ *
26152615+ * After using DNSServiceSetDispatchQueue on a DNSServiceRef, calling DNSServiceProcessResult
26162616+ * on the same DNSServiceRef will result in undefined behavior and should be avoided.
26172617+ *
26182618+ * Once the application successfully schedules a DNSServiceRef on a serial dispatch queue using
26192619+ * DNSServiceSetDispatchQueue, it cannot remove the DNSServiceRef from the dispatch queue, or use
26202620+ * DNSServiceSetDispatchQueue a second time to schedule the DNSServiceRef onto a different serial dispatch
26212621+ * queue. Once scheduled onto a dispatch queue a DNSServiceRef will deliver events to that queue until
26222622+ * the application no longer requires that operation and terminates it using DNSServiceRefDeallocate.
26232623+ *
26242624+ * service: DNSServiceRef that was allocated and returned to the application, when the
26252625+ * application calls one of the DNSService API.
26262626+ *
26272627+ * queue: dispatch queue where the application callback will be scheduled
26282628+ *
26292629+ * return value: Returns kDNSServiceErr_NoError on success.
26302630+ * Returns kDNSServiceErr_NoMemory if it cannot create a dispatch source
26312631+ * Returns kDNSServiceErr_BadParam if the service param is invalid or the
26322632+ * queue param is invalid
26332633+ */
26342634+26352635+DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue
26362636+(
26372637+ DNSServiceRef service,
26382638+ dispatch_queue_t queue
26392639+);
26402640+#endif //_DNS_SD_LIBDISPATCH
26412641+26422642+#if !defined(_WIN32)
26432643+typedef void (DNSSD_API *DNSServiceSleepKeepaliveReply)
26442644+(
26452645+ DNSServiceRef sdRef,
26462646+ DNSServiceErrorType errorCode,
26472647+ void *context
26482648+);
26492649+DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive
26502650+(
26512651+ DNSServiceRef *sdRef,
26522652+ DNSServiceFlags flags,
26532653+ int fd,
26542654+ unsigned int timeout,
26552655+ DNSServiceSleepKeepaliveReply callBack,
26562656+ void *context
26572657+);
26582658+#endif
26592659+26602660+#ifdef APPLE_OSX_mDNSResponder
26612661+/* DNSServiceCreateDelegateConnection()
26622662+ *
26632663+ * Create a delegate connection to the daemon allowing efficient registration of
26642664+ * multiple individual records.
26652665+ *
26662666+ * Parameters:
26672667+ *
26682668+ * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating
26692669+ * the reference (via DNSServiceRefDeallocate()) severs the
26702670+ * connection and deregisters all records registered on this connection.
26712671+ *
26722672+ * pid : Process ID of the delegate
26732673+ *
26742674+ * uuid: UUID of the delegate
26752675+ *
26762676+ * Note that only one of the two arguments (pid or uuid) can be specified. If pid
26772677+ * is zero, uuid will be assumed to be a valid value; otherwise pid will be used.
26782678+ *
26792679+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns
26802680+ * an error code indicating the specific failure that occurred (in which
26812681+ * case the DNSServiceRef is not initialized). kDNSServiceErr_NotAuth is
26822682+ * returned to indicate that the calling process does not have entitlements
26832683+ * to use this API.
26842684+ */
26852685+DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid);
26862686+#endif
26872687+26882688+#ifdef __APPLE_API_PRIVATE
26892689+26902690+#define kDNSServiceCompPrivateDNS "PrivateDNS"
26912691+#define kDNSServiceCompMulticastDNS "MulticastDNS"
26922692+26932693+#endif //__APPLE_API_PRIVATE
26942694+26952695+/* Some C compiler cleverness. We can make the compiler check certain things for us,
26962696+ * and report errors at compile-time if anything is wrong. The usual way to do this would
26972697+ * be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but
26982698+ * then you don't find out what's wrong until you run the software. This way, if the assertion
26992699+ * condition is false, the array size is negative, and the complier complains immediately.
27002700+ */
27012701+27022702+struct CompileTimeAssertionChecks_DNS_SD
27032703+{
27042704+ char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1];
27052705+};
27062706+27072707+#ifdef __cplusplus
27082708+}
27092709+#endif
27102710+27112711+#endif /* _DNS_SD_H */