]> git.saurik.com Git - apple/configd.git/blame - SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
configd-699.1.5.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCDNotifierInformViaCallback.c
CommitLineData
5958d7c0 1/*
78403150 2 * Copyright (c) 2000-2005, 2008-2014 Apple Inc. All rights reserved.
5958d7c0
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
009ee3c6 5 *
009ee3c6
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
5958d7c0
A
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
009ee3c6
A
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
5958d7c0
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
0fae82ee
A
24/*
25 * Modification History
26 *
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
29 *
30 * March 31, 2000 Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
a40a14f8
A
34#include <Availability.h>
35#include <TargetConditionals.h>
36#include <sys/cdefs.h>
a40a14f8 37#include <dispatch/dispatch.h>
5958d7c0
A
38#include <mach/mach.h>
39#include <mach/mach_error.h>
40
0fae82ee
A
41#include <SystemConfiguration/SystemConfiguration.h>
42#include <SystemConfiguration/SCPrivate.h>
43#include "SCDynamicStoreInternal.h"
5958d7c0 44#include "config.h" /* MiG generated file */
5958d7c0 45
6f870c06
A
46#if !TARGET_IPHONE_SIMULATOR || (defined(IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED) && (IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED >= 1090))
47#define HAVE_MACHPORT_GUARDS
48#endif
49
a40a14f8 50
edebe297
A
51static CFStringRef
52notifyMPCopyDescription(const void *info)
53{
54 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
55
56 return CFStringCreateWithFormat(NULL,
57 NULL,
58 CFSTR("<SCDynamicStore notification MP> {store = %p}"),
59 store);
60}
61
62
0fae82ee
A
63static void
64rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
5958d7c0 65{
6bb65964
A
66 mach_no_senders_notification_t *buf = msg;
67 mach_msg_id_t msgid = buf->not_header.msgh_id;
0fae82ee
A
68 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
69 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
70
71 if (msgid == MACH_NOTIFY_NO_SENDERS) {
72 /* the server died, disable additional callbacks */
dbf6a266 73#ifdef DEBUG
0fae82ee 74 SCLog(_sc_verbose, LOG_INFO, CFSTR(" rlsCallback(), notifier port closed"));
dbf6a266 75#endif /* DEBUG */
5958d7c0 76
0fae82ee 77#ifdef DEBUG
17d3ee29
A
78 if (port != storePrivate->rlsNotifyPort) {
79 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != rlsNotifyPort?"));
0fae82ee
A
80 }
81#endif /* DEBUG */
5958d7c0 82
6bb65964
A
83 /* re-establish notification and inform the client */
84 (void)__SCDynamicStoreReconnectNotifications(store);
0fae82ee 85 }
5958d7c0 86
0fae82ee 87 /* signal the real runloop source */
a40a14f8
A
88 if (storePrivate->rls != NULL) {
89 CFRunLoopSourceSignal(storePrivate->rls);
90 }
0fae82ee
A
91 return;
92}
5958d7c0 93
5958d7c0 94
0fae82ee
A
95static void
96rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
97{
98 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
99 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
5958d7c0 100
dbf6a266 101#ifdef DEBUG
a40a14f8
A
102 SCLog(_sc_verbose, LOG_DEBUG,
103 CFSTR("schedule notifications for mode %@"),
104 (rl != NULL) ? mode : CFSTR("libdispatch"));
dbf6a266 105#endif /* DEBUG */
0fae82ee 106
6bb65964
A
107 if (storePrivate->rlList == NULL) {
108 CFMachPortContext context = { 0
109 , (void *)store
110 , CFRetain
111 , CFRelease
112 , notifyMPCopyDescription
113 };
6f870c06 114 kern_return_t kr;
0fae82ee 115 mach_port_t oldNotify;
6f870c06
A
116#ifdef HAVE_MACHPORT_GUARDS
117 mach_port_options_t opts;
118#endif // HAVE_MACHPORT_GUARDS
0fae82ee
A
119 mach_port_t port;
120 int sc_status;
0fae82ee 121
dbf6a266 122#ifdef DEBUG
0fae82ee 123 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" activate callback runloop source"));
dbf6a266 124#endif /* DEBUG */
0fae82ee 125
6f870c06
A
126 /* allocate a mach port for the SCDynamicStore notifications */
127
128 retry_allocate :
129
130#ifdef HAVE_MACHPORT_GUARDS
131 bzero(&opts, sizeof(opts));
132 opts.flags = MPO_CONTEXT_AS_GUARD|MPO_INSERT_SEND_RIGHT;
133
134 kr = mach_port_construct(mach_task_self(), &opts, store, &port);
135#else // HAVE_MACHPORT_GUARDS
136 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
137#endif // HAVE_MACHPORT_GUARDS
138
139 if (kr != KERN_SUCCESS) {
140 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule could not allocate mach port: %s"), mach_error_string(kr));
141 if ((kr == KERN_NO_SPACE) || (kr == KERN_RESOURCE_SHORTAGE)) {
142 sleep(1);
143 goto retry_allocate;
144 } else {
145 return;
146 }
5958d7c0
A
147 }
148
6f870c06
A
149#ifndef HAVE_MACHPORT_GUARDS
150 kr = mach_port_insert_right(mach_task_self(),
151 port,
152 port,
153 MACH_MSG_TYPE_MAKE_SEND);
154 if (kr != KERN_SUCCESS) {
a40a14f8
A
155 /*
156 * We can't insert a send right into our own port! This should
157 * only happen if someone stomped on OUR port (so let's leave
158 * the port alone).
159 */
6f870c06 160 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_insert_right(): %s"), mach_error_string(kr));
0fae82ee 161 return;
5958d7c0 162 }
6f870c06 163#endif // HAVE_MACHPORT_GUARDS
5958d7c0 164
0fae82ee 165 /* Request a notification when/if the server dies */
6f870c06
A
166 kr = mach_port_request_notification(mach_task_self(),
167 port,
168 MACH_NOTIFY_NO_SENDERS,
169 1,
170 port,
171 MACH_MSG_TYPE_MAKE_SEND_ONCE,
172 &oldNotify);
173 if (kr != KERN_SUCCESS) {
a40a14f8
A
174 /*
175 * We can't request a notification for our own port! This should
176 * only happen if someone stomped on OUR port (so let's leave
177 * the port alone).
178 */
6f870c06 179 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_request_notification(): %s"), mach_error_string(kr));
0fae82ee
A
180 return;
181 }
5958d7c0 182
0fae82ee 183 if (oldNotify != MACH_PORT_NULL) {
a40a14f8 184 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule(): oldNotify != MACH_PORT_NULL"));
5958d7c0
A
185 }
186
6bb65964
A
187 retry :
188
a40a14f8 189 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule", port);
6f870c06 190 kr = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
17d3ee29
A
191
192 if (__SCDynamicStoreCheckRetryAndHandleError(store,
6f870c06 193 kr,
17d3ee29
A
194 &sc_status,
195 "rlsSchedule notifyviaport()")) {
196 goto retry;
197 }
198
6f870c06
A
199 if (kr != KERN_SUCCESS) {
200 if ((kr == MACH_SEND_INVALID_DEST) || (kr == MIG_SERVER_DIED)) {
a40a14f8
A
201 /* remove the send right that we tried (but failed) to pass to the server */
202 (void) mach_port_deallocate(mach_task_self(), port);
203 }
204
205 /* remove our receive right */
6f870c06
A
206#ifdef HAVE_MACHPORT_GUARDS
207 (void) mach_port_destruct(mach_task_self(), port, 0, store);
208#else // HAVE_MACHPORT_GUARDS
a40a14f8 209 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
6f870c06 210#endif // HAVE_MACHPORT_GUARDS
0fae82ee
A
211 return;
212 }
5958d7c0 213
6bb65964
A
214 if (sc_status != kSCStatusOK) {
215 /* something [else] didn't work, remove our receive right */
6f870c06
A
216#ifdef HAVE_MACHPORT_GUARDS
217 (void) mach_port_destruct(mach_task_self(), port, 0, store);
218#else // HAVE_MACHPORT_GUARDS
a40a14f8 219 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
6f870c06 220#endif // HAVE_MACHPORT_GUARDS
a40a14f8
A
221 return;
222 }
6bb65964
A
223
224 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after notifyviaport)", port);
17d3ee29 225 storePrivate->rlsNotifyPort = _SC_CFMachPortCreateWithPort("SCDynamicStore",
5e9ce69e
A
226 port,
227 rlsCallback,
228 &context);
17d3ee29 229 storePrivate->rlsNotifyRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->rlsNotifyPort, 0);
6bb65964
A
230
231 storePrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
0fae82ee 232 }
5958d7c0 233
17d3ee29 234 if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
6bb65964
A
235 if (!_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
236 /*
237 * if we are not already scheduled with this runLoop / runLoopMode
238 */
17d3ee29
A
239 CFRunLoopAddSource(rl, storePrivate->rlsNotifyRLS, mode);
240 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->rlsNotifyPort));
6bb65964
A
241 }
242
243 _SC_schedule(store, rl, mode, storePrivate->rlList);
009ee3c6
A
244 }
245
0fae82ee 246 return;
5958d7c0
A
247}
248
249
0fae82ee
A
250static void
251rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
5958d7c0 252{
6bb65964 253 CFIndex n = 0;
0fae82ee
A
254 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
255 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
5958d7c0 256
dbf6a266 257#ifdef DEBUG
a40a14f8
A
258 SCLog(_sc_verbose, LOG_DEBUG,
259 CFSTR("cancel notifications for mode %@"),
260 (rl != NULL) ? mode : CFSTR("libdispatch"));
dbf6a266 261#endif /* DEBUG */
5958d7c0 262
17d3ee29 263 if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
6bb65964
A
264 if (_SC_unschedule(store, rl, mode, storePrivate->rlList, FALSE)) {
265 /*
266 * if currently scheduled on this runLoop / runLoopMode
267 */
268 n = CFArrayGetCount(storePrivate->rlList);
269 if (n == 0 || !_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
270 /*
271 * if we are no longer scheduled to receive notifications for
272 * this runLoop / runLoopMode
273 */
17d3ee29 274 CFRunLoopRemoveSource(rl, storePrivate->rlsNotifyRLS, mode);
6bb65964
A
275 }
276 }
009ee3c6 277 }
a5f60add 278
6bb65964 279 if (n == 0) {
0fae82ee 280 int sc_status;
6f870c06 281 kern_return_t kr;
5958d7c0 282
dbf6a266 283#ifdef DEBUG
0fae82ee 284 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" cancel callback runloop source"));
dbf6a266 285#endif /* DEBUG */
17d3ee29 286 __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
a40a14f8 287 "*** rlsCancel",
17d3ee29 288 CFMachPortGetPort(storePrivate->rlsNotifyPort));
5958d7c0 289
17d3ee29
A
290 if (storePrivate->rlList != NULL) {
291 CFRelease(storePrivate->rlList);
292 storePrivate->rlList = NULL;
293 }
6bb65964 294
17d3ee29 295 if (storePrivate->rlsNotifyRLS != NULL) {
dbf6a266 296 /* invalidate & remove the run loop source */
17d3ee29
A
297 CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS);
298 CFRelease(storePrivate->rlsNotifyRLS);
299 storePrivate->rlsNotifyRLS = NULL;
009ee3c6 300 }
5958d7c0 301
17d3ee29 302 if (storePrivate->rlsNotifyPort != NULL) {
5e9ce69e
A
303 mach_port_t mp;
304
305 mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
17d3ee29 306 __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
5e9ce69e
A
307 "*** rlsCancel (before invalidating/releasing CFMachPort)",
308 mp);
309
310 /* invalidate and release port */
17d3ee29
A
311 CFMachPortInvalidate(storePrivate->rlsNotifyPort);
312 CFRelease(storePrivate->rlsNotifyPort);
313 storePrivate->rlsNotifyPort = NULL;
5e9ce69e
A
314
315 /* and, finally, remove our receive right */
6f870c06
A
316#ifdef HAVE_MACHPORT_GUARDS
317 (void) mach_port_destruct(mach_task_self(), mp, 0, store);
318#else // HAVE_MACHPORT_GUARDS
319 (void) mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
320#endif // HAVE_MACHPORT_GUARDS
009ee3c6 321 }
5958d7c0 322
dbf6a266 323 if (storePrivate->server != MACH_PORT_NULL) {
6f870c06 324 kr = notifycancel(storePrivate->server, (int *)&sc_status);
17d3ee29
A
325
326 (void) __SCDynamicStoreCheckRetryAndHandleError(store,
6f870c06 327 kr,
17d3ee29
A
328 &sc_status,
329 "rlsCancel notifycancel()");
330
6f870c06 331 if (kr != KERN_SUCCESS) {
009ee3c6
A
332 return;
333 }
0fae82ee 334 }
5958d7c0 335 }
a40a14f8 336
0fae82ee
A
337 return;
338}
5958d7c0 339
009ee3c6 340
0fae82ee
A
341static void
342rlsPerform(void *info)
343{
344 CFArrayRef changedKeys;
345 void *context_info;
346 void (*context_release)(const void *);
347 SCDynamicStoreCallBack rlsFunction;
348 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
349 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
350
dbf6a266
A
351#ifdef DEBUG
352 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function"));
353#endif /* DEBUG */
0fae82ee
A
354
355 changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
6bb65964
A
356 if (storePrivate->disconnectForceCallBack) {
357 storePrivate->disconnectForceCallBack = FALSE;
358 if (changedKeys == NULL) {
359 changedKeys = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
360 }
361 } else {
362 if (changedKeys == NULL) {
363 /* if no changes or something happened to the server */
364 return;
365 } else if (CFArrayGetCount(changedKeys) == 0) {
366 goto done;
367 }
dbf6a266
A
368 }
369
0fae82ee
A
370 rlsFunction = storePrivate->rlsFunction;
371
a40a14f8 372 if (storePrivate->rlsContext.retain != NULL) {
0fae82ee
A
373 context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info);
374 context_release = storePrivate->rlsContext.release;
375 } else {
376 context_info = storePrivate->rlsContext.info;
377 context_release = NULL;
378 }
5e9ce69e
A
379 if (rlsFunction != NULL) {
380 (*rlsFunction)(store, changedKeys, context_info);
381 }
382 if (context_release != NULL) {
0fae82ee 383 context_release(context_info);
5958d7c0 384 }
5958d7c0 385
dbf6a266
A
386 done :
387
0fae82ee
A
388 CFRelease(changedKeys);
389 return;
390}
5958d7c0 391
0fae82ee 392
a5f60add
A
393static CFTypeRef
394rlsRetain(CFTypeRef cf)
395{
396 SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
397 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
398
6bb65964
A
399 switch (storePrivate->notifyStatus) {
400 case NotifierNotRegistered :
401 /* mark RLS active */
402 storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
403 /* keep a reference to the store */
404 CFRetain(store);
405 break;
406 case Using_NotifierInformViaRunLoop :
407 break;
408 default :
409 SCLog(TRUE, LOG_ERR, CFSTR("rlsRetain() error: notifyStatus=%d"), storePrivate->notifyStatus);
410 break;
a5f60add
A
411 }
412
413 return cf;
414}
415
009ee3c6 416
a5f60add
A
417static void
418rlsRelease(CFTypeRef cf)
419{
420 SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
421 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
422
6bb65964
A
423 switch (storePrivate->notifyStatus) {
424 case NotifierNotRegistered :
425 break;
426 case Using_NotifierInformViaRunLoop :
427 /* mark RLS inactive */
428 storePrivate->notifyStatus = NotifierNotRegistered;
429 storePrivate->rls = NULL;
a5f60add 430
6bb65964
A
431 /* release our reference to the store */
432 CFRelease(store);
433 break;
434 default :
435 SCLog(TRUE, LOG_ERR, CFSTR("rlsRelease() error: notifyStatus=%d"), storePrivate->notifyStatus);
436 break;
437 }
a5f60add
A
438
439 return;
440}
441
442
edebe297
A
443static CFStringRef
444rlsCopyDescription(const void *info)
445{
a40a14f8
A
446 CFMutableStringRef result;
447 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
edebe297
A
448 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
449
450 result = CFStringCreateMutable(NULL, 0);
451 CFStringAppendFormat(result, NULL, CFSTR("<SCDynamicStore RLS> {"));
452 CFStringAppendFormat(result, NULL, CFSTR("store = %p"), store);
453 if (storePrivate->notifyStatus == Using_NotifierInformViaRunLoop) {
454 CFStringRef description = NULL;
455
456 CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->rlsFunction);
457
458 if ((storePrivate->rlsContext.info != NULL) && (storePrivate->rlsContext.copyDescription != NULL)) {
459 description = (*storePrivate->rlsContext.copyDescription)(storePrivate->rlsContext.info);
460 }
461 if (description == NULL) {
462 description = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCDynamicStore context %p>"), storePrivate->rlsContext.info);
463 }
464 if (description == NULL) {
465 description = CFRetain(CFSTR("<no description>"));
466 }
467 CFStringAppendFormat(result, NULL, CFSTR(", context = %@"), description);
468 CFRelease(description);
edebe297
A
469 }
470 CFStringAppendFormat(result, NULL, CFSTR("}"));
471
472 return result;
473}
474
475
0fae82ee
A
476CFRunLoopSourceRef
477SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator,
478 SCDynamicStoreRef store,
479 CFIndex order)
480{
a5f60add 481 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
0fae82ee 482
dbf6a266 483 if (store == NULL) {
0fae82ee
A
484 /* sorry, you must provide a session */
485 _SCErrorSet(kSCStatusNoStoreSession);
486 return NULL;
5958d7c0
A
487 }
488
0fae82ee
A
489 if (storePrivate->server == MACH_PORT_NULL) {
490 /* sorry, you must have an open session to play */
491 _SCErrorSet(kSCStatusNoStoreServer);
492 return NULL;
5958d7c0
A
493 }
494
0fae82ee
A
495 switch (storePrivate->notifyStatus) {
496 case NotifierNotRegistered :
497 case Using_NotifierInformViaRunLoop :
a5f60add 498 /* OK to enable runloop notification */
0fae82ee
A
499 break;
500 default :
501 /* sorry, you can only have one notification registered at once */
502 _SCErrorSet(kSCStatusNotifierActive);
503 return NULL;
5958d7c0
A
504 }
505
dbf6a266 506 if (storePrivate->rls != NULL) {
a5f60add
A
507 CFRetain(storePrivate->rls);
508 } else {
509 CFRunLoopSourceContext context = { 0 // version
510 , (void *)store // info
511 , rlsRetain // retain
512 , rlsRelease // release
edebe297 513 , rlsCopyDescription // copyDescription
a5f60add
A
514 , CFEqual // equal
515 , CFHash // hash
516 , rlsSchedule // schedule
517 , rlsCancel // cancel
518 , rlsPerform // perform
519 };
0fae82ee
A
520
521 storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context);
a40a14f8
A
522 if (storePrivate->rls == NULL) {
523 _SCErrorSet(kSCStatusFailed);
524 }
0fae82ee
A
525 }
526
a40a14f8
A
527 return storePrivate->rls;
528}
529
530
a40a14f8
A
531Boolean
532SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store, dispatch_queue_t queue)
533{
17d3ee29
A
534 dispatch_group_t drainGroup = NULL;
535 dispatch_queue_t drainQueue = NULL;
e812fdb8 536 dispatch_group_t group = NULL;
17d3ee29 537 mach_port_t mp;
a40a14f8 538 Boolean ok = FALSE;
17d3ee29 539 dispatch_source_t source;
a40a14f8
A
540 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
541
542 if (store == NULL) {
17d3ee29 543 // sorry, you must provide a session
a40a14f8
A
544 _SCErrorSet(kSCStatusNoStoreSession);
545 return FALSE;
546 }
547
17d3ee29
A
548 if (queue == NULL) {
549 if (storePrivate->dispatchQueue == NULL) {
a40a14f8
A
550 _SCErrorSet(kSCStatusInvalidArgument);
551 return FALSE;
552 }
553
17d3ee29
A
554 ok = TRUE;
555 goto cleanup;
556 }
a40a14f8 557
17d3ee29
A
558 if (storePrivate->server == MACH_PORT_NULL) {
559 // sorry, you must have an open session to play
560 _SCErrorSet(kSCStatusNoStoreServer);
561 return FALSE;
562 }
6bb65964 563
17d3ee29
A
564 if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL)) {
565 _SCErrorSet(kSCStatusInvalidArgument);
566 return FALSE;
567 }
568
569 if (storePrivate->notifyStatus != NotifierNotRegistered) {
570 // sorry, you can only have one notification registered at once...
571 _SCErrorSet(kSCStatusNotifierActive);
572 return FALSE;
573 }
574
575 /*
576 * mark our using of the SCDynamicStore notifications, create and schedule
577 * the notification port (storePrivate->rlsNotifyPort), and a bunch of other
578 * "setup"
579 */
580 storePrivate->notifyStatus = Using_NotifierInformViaDispatch;
581 rlsSchedule((void*)store, NULL, NULL);
582 if (storePrivate->rlsNotifyPort == NULL) {
583 /* if we could not schedule the notification */
584 _SCErrorSet(kSCStatusFailed);
585 goto cleanup;
586 }
587
588 // retain the dispatch queue
589 storePrivate->dispatchQueue = queue;
590 dispatch_retain(storePrivate->dispatchQueue);
591
592 //
593 // We've taken a reference to the callers dispatch_queue and we
594 // want to hold on to that reference until we've processed any/all
595 // notifications. To facilitate this we create a group, dispatch
596 // any notification blocks to via that group, and when the caller
597 // has told us to stop the notifications (unschedule) we wait for
598 // the group to empty and use the group's finalizer to release
599 // our reference to the SCDynamicStore.
600 //
e812fdb8
A
601 group = dispatch_group_create();
602 storePrivate->dispatchGroup = group;
17d3ee29
A
603 CFRetain(store);
604 dispatch_set_context(storePrivate->dispatchGroup, (void *)store);
605 dispatch_set_finalizer_f(storePrivate->dispatchGroup, (dispatch_function_t)CFRelease);
606
607 // create a dispatch source for the mach notifications
608 mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
609 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue);
610 if (source == NULL) {
611 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_source_create() failed"));
612 _SCErrorSet(kSCStatusFailed);
613 goto cleanup;
614 }
615
616 dispatch_source_set_event_handler(source, ^{
617 kern_return_t kr;
618 mach_msg_id_t msgid;
619 union {
620 u_int8_t buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE];
621 mach_msg_empty_rcv_t msg;
622 mach_no_senders_notification_t no_senders;
623 } notify_msg;
624
625 kr = mach_msg(&notify_msg.msg.header, // msg
626 MACH_RCV_MSG, // options
627 0, // send_size
628 sizeof(notify_msg), // rcv_size
629 mp, // rcv_name
630 MACH_MSG_TIMEOUT_NONE, // timeout
631 MACH_PORT_NULL); // notify
632 if (kr != KERN_SUCCESS) {
633 SCLog(TRUE, LOG_ERR,
634 CFSTR("SCDynamicStore notification handler, kr=0x%x"),
635 kr);
636 return;
a40a14f8 637 }
17d3ee29
A
638
639 msgid = notify_msg.msg.header.msgh_id;
640
641 CFRetain(store);
e812fdb8 642 dispatch_group_async(group, queue, ^{
17d3ee29
A
643 if (msgid == MACH_NOTIFY_NO_SENDERS) {
644 // re-establish notification and inform the client
645 (void)__SCDynamicStoreReconnectNotifications(store);
646 }
647 rlsPerform(storePrivate);
648 CFRelease(store);
d0784775 649 });
17d3ee29 650 });
a40a14f8 651
17d3ee29
A
652 dispatch_source_set_cancel_handler(source, ^{
653 dispatch_release(source);
654 });
a40a14f8 655
17d3ee29
A
656 storePrivate->dispatchSource = source;
657 dispatch_resume(source);
658
659 return TRUE;
a40a14f8
A
660
661 cleanup :
662
17d3ee29
A
663 CFRetain(store);
664
665 if (storePrivate->dispatchSource != NULL) {
666 dispatch_source_cancel(storePrivate->dispatchSource);
667 storePrivate->dispatchSource = NULL;
a40a14f8 668 }
17d3ee29
A
669 drainGroup = storePrivate->dispatchGroup;
670 storePrivate->dispatchGroup = NULL;
671 drainQueue = storePrivate->dispatchQueue;
a40a14f8 672 storePrivate->dispatchQueue = NULL;
17d3ee29 673
a40a14f8 674 rlsCancel((void*)store, NULL, NULL);
17d3ee29
A
675
676 if (drainGroup != NULL) {
677 dispatch_group_notify(drainGroup, drainQueue, ^{
678 // release group/queue references
679 dispatch_release(drainQueue);
680 dispatch_release(drainGroup); // releases our store reference
681 });
682 }
683
a40a14f8
A
684 storePrivate->notifyStatus = NotifierNotRegistered;
685
17d3ee29 686 CFRelease(store);
a40a14f8
A
687
688 return ok;
a5f60add 689}