]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
configd-289.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCDNotifierInformViaCallback.c
1 /*
2 * Copyright (c) 2000-2005, 2008, 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
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
34 #include <Availability.h>
35 #include <TargetConditionals.h>
36 #include <sys/cdefs.h>
37 #if !TARGET_OS_IPHONE
38 #include <dispatch/dispatch.h>
39 #endif // !TARGET_OS_IPHONE
40 #include <mach/mach.h>
41 #include <mach/mach_error.h>
42
43 #include <SystemConfiguration/SystemConfiguration.h>
44 #include <SystemConfiguration/SCPrivate.h>
45 #include "SCDynamicStoreInternal.h"
46 #include "config.h" /* MiG generated file */
47
48
49 static void
50 informCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
51 {
52 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
53 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
54 mach_msg_empty_rcv_t *buf = msg;
55 mach_msg_id_t msgid = buf->header.msgh_id;
56 SCDynamicStoreCallBack_v1 cbFunc = storePrivate->callbackFunction;
57 void *cbArg = storePrivate->callbackArgument;
58
59 if (msgid == MACH_NOTIFY_NO_SENDERS) {
60 /* the server died, disable additional callbacks */
61 #ifdef DEBUG
62 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" notifier port closed, disabling notifier"));
63 #endif /* DEBUG */
64 } else if (cbFunc == NULL) {
65 /* there is no (longer) a callback function, disable additional callbacks */
66 #ifdef DEBUG
67 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" no callback function, disabling notifier"));
68 #endif /* DEBUG */
69 } else {
70 #ifdef DEBUG
71 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function"));
72 #endif /* DEBUG */
73 if ((*cbFunc)(store, cbArg)) {
74 /*
75 * callback function returned success.
76 */
77 return;
78 } else {
79 #ifdef DEBUG
80 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" callback returned error, disabling notifier"));
81 #endif /* DEBUG */
82 }
83 }
84
85 #ifdef DEBUG
86 if (port != storePrivate->callbackPort) {
87 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("informCallback, why is port != callbackPort?"));
88 }
89 #endif /* DEBUG */
90
91 /* invalidate the run loop source */
92 if (storePrivate->callbackRLS != NULL) {
93 CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
94 CFRelease(storePrivate->callbackRLS);
95 storePrivate->callbackRLS = NULL;
96 }
97
98 /* invalidate port */
99 if (storePrivate->callbackPort != NULL) {
100 __MACH_PORT_DEBUG(TRUE, "*** informCallback", CFMachPortGetPort(storePrivate->callbackPort));
101 CFMachPortInvalidate(storePrivate->callbackPort);
102 CFRelease(storePrivate->callbackPort);
103 storePrivate->callbackPort = NULL;
104 }
105
106 /* disable notifier */
107 storePrivate->notifyStatus = NotifierNotRegistered;
108 storePrivate->callbackArgument = NULL;
109 storePrivate->callbackFunction = NULL;
110
111 return;
112 }
113
114
115 static CFStringRef
116 notifyMPCopyDescription(const void *info)
117 {
118 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
119
120 return CFStringCreateWithFormat(NULL,
121 NULL,
122 CFSTR("<SCDynamicStore notification MP> {store = %p}"),
123 store);
124 }
125
126
127 Boolean
128 SCDynamicStoreNotifyCallback(SCDynamicStoreRef store,
129 CFRunLoopRef runLoop,
130 SCDynamicStoreCallBack_v1 func,
131 void *arg)
132 {
133 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
134 kern_return_t status;
135 mach_port_t port;
136 mach_port_t oldNotify;
137 int sc_status;
138 CFMachPortContext context = { 0
139 , (void *)store
140 , CFRetain
141 , CFRelease
142 , notifyMPCopyDescription
143 };
144
145 if (store == NULL) {
146 /* sorry, you must provide a session */
147 _SCErrorSet(kSCStatusNoStoreSession);
148 return FALSE;
149 }
150
151 if (storePrivate->server == MACH_PORT_NULL) {
152 /* sorry, you must have an open session to play */
153 _SCErrorSet(kSCStatusNoStoreServer);
154 return FALSE;
155 }
156
157 if (storePrivate->notifyStatus != NotifierNotRegistered) {
158 /* sorry, you can only have one notification registered at once */
159 _SCErrorSet(kSCStatusNotifierActive);
160 return FALSE;
161 }
162
163 /* Allocating port (for server response) */
164 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
165 if (status != KERN_SUCCESS) {
166 SCLog(TRUE, LOG_ERR, CFSTR("mach_port_allocate(): %s"), mach_error_string(status));
167 _SCErrorSet(status);
168 return FALSE;
169 }
170
171 status = mach_port_insert_right(mach_task_self(),
172 port,
173 port,
174 MACH_MSG_TYPE_MAKE_SEND);
175 if (status != KERN_SUCCESS) {
176 /*
177 * We can't insert a send right into our own port! This should
178 * only happen if someone stomped on OUR port (so let's leave
179 * the port alone).
180 */
181 SCLog(TRUE, LOG_ERR, CFSTR("mach_port_insert_right(): %s"), mach_error_string(status));
182 _SCErrorSet(status);
183 return FALSE;
184 }
185
186 /* Request a notification when/if the server dies */
187 status = mach_port_request_notification(mach_task_self(),
188 port,
189 MACH_NOTIFY_NO_SENDERS,
190 1,
191 port,
192 MACH_MSG_TYPE_MAKE_SEND_ONCE,
193 &oldNotify);
194 if (status != KERN_SUCCESS) {
195 /*
196 * We can't request a notification for our own port! This should
197 * only happen if someone stomped on OUR port (so let's leave
198 * the port alone).
199 */
200 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback mach_port_request_notification(): %s"), mach_error_string(status));
201 _SCErrorSet(status);
202 return FALSE;
203 }
204
205 if (oldNotify != MACH_PORT_NULL) {
206 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback(): oldNotify != MACH_PORT_NULL"));
207 }
208
209 /* Requesting notification via mach port */
210 status = notifyviaport(storePrivate->server,
211 port,
212 0,
213 (int *)&sc_status);
214
215 if (status != KERN_SUCCESS) {
216 if (status == MACH_SEND_INVALID_DEST) {
217 /* the server's gone and our session port's dead, remove the dead name right */
218 (void) mach_port_deallocate(mach_task_self(), storePrivate->server);
219 } else {
220 /* we got an unexpected error, leave the [session] port alone */
221 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback notifyviaport(): %s"), mach_error_string(status));
222 }
223 storePrivate->server = MACH_PORT_NULL;
224
225 if (status == MACH_SEND_INVALID_DEST) {
226 /* remove the send right that we tried (but failed) to pass to the server */
227 (void) mach_port_deallocate(mach_task_self(), port);
228 }
229
230 /* remove our receive right */
231 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
232 _SCErrorSet(status);
233 return FALSE;
234 }
235
236 if (sc_status != kSCStatusOK) {
237 _SCErrorSet(sc_status);
238 return FALSE;
239 }
240
241 /* set notifier active */
242 storePrivate->notifyStatus = Using_NotifierInformViaCallback;
243
244 /* Creating/adding a run loop source for the port */
245 __MACH_PORT_DEBUG(TRUE, "*** SCDynamicStoreNotifyCallback", port);
246 storePrivate->callbackArgument = arg;
247 storePrivate->callbackFunction = func;
248 storePrivate->callbackPort = CFMachPortCreateWithPort(NULL, port, informCallback, &context, NULL);
249 storePrivate->callbackRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
250 CFRunLoopAddSource(runLoop, storePrivate->callbackRLS, kCFRunLoopDefaultMode);
251
252 return TRUE;
253 }
254
255
256 static void
257 rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
258 {
259 mach_msg_empty_rcv_t *buf = msg;
260 mach_msg_id_t msgid = buf->header.msgh_id;
261 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
262 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
263
264 if (msgid == MACH_NOTIFY_NO_SENDERS) {
265 /* the server died, disable additional callbacks */
266 #ifdef DEBUG
267 SCLog(_sc_verbose, LOG_INFO, CFSTR(" rlsCallback(), notifier port closed"));
268 #endif /* DEBUG */
269
270 #ifdef DEBUG
271 if (port != storePrivate->callbackPort) {
272 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != callbackPort?"));
273 }
274 #endif /* DEBUG */
275
276 /* invalidate the run loop source(s) */
277 if (storePrivate->callbackRLS != NULL) {
278 CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
279 CFRelease(storePrivate->callbackRLS);
280 storePrivate->callbackRLS = NULL;
281 }
282
283 /* invalidate port */
284 if (storePrivate->callbackPort != NULL) {
285 __MACH_PORT_DEBUG(TRUE, "*** rlsCallback w/MACH_NOTIFY_NO_SENDERS", CFMachPortGetPort(storePrivate->callbackPort));
286 CFMachPortInvalidate(storePrivate->callbackPort);
287 CFRelease(storePrivate->callbackPort);
288 storePrivate->callbackPort = NULL;
289 }
290
291 return;
292 }
293
294 /* signal the real runloop source */
295 if (storePrivate->rls != NULL) {
296 CFRunLoopSourceSignal(storePrivate->rls);
297 }
298 return;
299 }
300
301
302 static void
303 portInvalidate(CFMachPortRef port, void *info) {
304 mach_port_t mp = CFMachPortGetPort(port);
305
306 __MACH_PORT_DEBUG(TRUE, "*** portInvalidate", mp);
307 /* remove our receive right */
308 (void)mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
309 }
310
311
312 static void
313 rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
314 {
315 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
316 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
317
318 #ifdef DEBUG
319 SCLog(_sc_verbose, LOG_DEBUG,
320 CFSTR("schedule notifications for mode %@"),
321 (rl != NULL) ? mode : CFSTR("libdispatch"));
322 #endif /* DEBUG */
323
324 if (storePrivate->rlsRefs++ == 0) {
325 CFMachPortContext context = { 0
326 , (void *)store
327 , CFRetain
328 , CFRelease
329 , notifyMPCopyDescription
330 };
331 mach_port_t oldNotify;
332 mach_port_t port;
333 int sc_status;
334 kern_return_t status;
335
336 #ifdef DEBUG
337 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" activate callback runloop source"));
338 #endif /* DEBUG */
339
340 /* Allocating port (for server response) */
341 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
342 if (status != KERN_SUCCESS) {
343 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_allocate(): %s"), mach_error_string(status));
344 return;
345 }
346
347 status = mach_port_insert_right(mach_task_self(),
348 port,
349 port,
350 MACH_MSG_TYPE_MAKE_SEND);
351 if (status != KERN_SUCCESS) {
352 /*
353 * We can't insert a send right into our own port! This should
354 * only happen if someone stomped on OUR port (so let's leave
355 * the port alone).
356 */
357 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_insert_right(): %s"), mach_error_string(status));
358 return;
359 }
360
361 /* Request a notification when/if the server dies */
362 status = mach_port_request_notification(mach_task_self(),
363 port,
364 MACH_NOTIFY_NO_SENDERS,
365 1,
366 port,
367 MACH_MSG_TYPE_MAKE_SEND_ONCE,
368 &oldNotify);
369 if (status != KERN_SUCCESS) {
370 /*
371 * We can't request a notification for our own port! This should
372 * only happen if someone stomped on OUR port (so let's leave
373 * the port alone).
374 */
375 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_request_notification(): %s"), mach_error_string(status));
376 return;
377 }
378
379 if (oldNotify != MACH_PORT_NULL) {
380 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule(): oldNotify != MACH_PORT_NULL"));
381 }
382
383 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule", port);
384 status = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
385 if (status != KERN_SUCCESS) {
386 if (status == MACH_SEND_INVALID_DEST) {
387 /* the server's gone and our session port's dead, remove the dead name right */
388 (void) mach_port_deallocate(mach_task_self(), storePrivate->server);
389 } else {
390 /* we got an unexpected error, leave the [session] port alone */
391 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule notifyviaport(): %s"), mach_error_string(status));
392 }
393 storePrivate->server = MACH_PORT_NULL;
394
395 if (status == MACH_SEND_INVALID_DEST) {
396 /* remove the send right that we tried (but failed) to pass to the server */
397 (void) mach_port_deallocate(mach_task_self(), port);
398 }
399
400 /* remove our receive right */
401 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
402 return;
403 }
404
405 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after notifyviaport)", port);
406 storePrivate->callbackPort = CFMachPortCreateWithPort(NULL, port, rlsCallback, &context, NULL);
407 if (storePrivate->callbackPort == NULL) {
408 SCLog(TRUE, LOG_ERR, CFSTR("*** CFMachPortCreateWithPort returned NULL while attempting to schedule"));
409 SCLog(TRUE, LOG_ERR, CFSTR("*** a SCDynamicStore notification. Did this process call \"fork\" without"));
410 SCLog(TRUE, LOG_ERR, CFSTR("*** calling \"exec\""));
411
412 /* the server's gone and our session port's dead, remove the dead name right */
413 (void) mach_port_deallocate(mach_task_self(), storePrivate->server);
414 storePrivate->server = MACH_PORT_NULL;
415
416 /* remove our receive right */
417 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
418 return;
419 }
420 CFMachPortSetInvalidationCallBack(storePrivate->callbackPort, portInvalidate);
421 storePrivate->callbackRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
422 }
423
424 if ((rl != NULL) && (storePrivate->callbackRLS != NULL)) {
425 CFRunLoopAddSource(rl, storePrivate->callbackRLS, mode);
426 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->callbackPort));
427 }
428
429 return;
430 }
431
432
433 static void
434 rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
435 {
436 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
437 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
438
439 #ifdef DEBUG
440 SCLog(_sc_verbose, LOG_DEBUG,
441 CFSTR("cancel notifications for mode %@"),
442 (rl != NULL) ? mode : CFSTR("libdispatch"));
443 #endif /* DEBUG */
444
445 if ((rl != NULL) && (storePrivate->callbackRLS != NULL)) {
446 CFRunLoopRemoveSource(rl, storePrivate->callbackRLS, mode);
447 }
448
449 if (--storePrivate->rlsRefs == 0) {
450 int sc_status;
451 kern_return_t status;
452
453 #ifdef DEBUG
454 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" cancel callback runloop source"));
455 #endif /* DEBUG */
456 __MACH_PORT_DEBUG((storePrivate->callbackPort != NULL),
457 "*** rlsCancel",
458 CFMachPortGetPort(storePrivate->callbackPort));
459
460 if (storePrivate->callbackRLS != NULL) {
461 /* invalidate & remove the run loop source */
462 CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
463 CFRelease(storePrivate->callbackRLS);
464 storePrivate->callbackRLS = NULL;
465 }
466
467 if (storePrivate->callbackPort != NULL) {
468 /* invalidate port */
469 __MACH_PORT_DEBUG((storePrivate->callbackPort != NULL),
470 "*** rlsCancel (before invalidating CFMachPort)",
471 CFMachPortGetPort(storePrivate->callbackPort));
472 CFMachPortInvalidate(storePrivate->callbackPort);
473 CFRelease(storePrivate->callbackPort);
474 storePrivate->callbackPort = NULL;
475 }
476
477 if (storePrivate->server != MACH_PORT_NULL) {
478 status = notifycancel(storePrivate->server, (int *)&sc_status);
479 if (status != KERN_SUCCESS) {
480 if (status == MACH_SEND_INVALID_DEST) {
481 /* the server's gone and our session port's dead, remove the dead name right */
482 (void) mach_port_deallocate(mach_task_self(), storePrivate->server);
483 } else {
484 /* we got an unexpected error, leave the [session] port alone */
485 SCLog(TRUE, LOG_ERR, CFSTR("rlsCancel notifycancel(): %s"), mach_error_string(status));
486 }
487 storePrivate->server = MACH_PORT_NULL;
488 return;
489 }
490 }
491 }
492
493 return;
494 }
495
496
497 static void
498 rlsPerform(void *info)
499 {
500 CFArrayRef changedKeys;
501 void *context_info;
502 void (*context_release)(const void *);
503 SCDynamicStoreCallBack rlsFunction;
504 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
505 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
506
507 #ifdef DEBUG
508 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function"));
509 #endif /* DEBUG */
510
511 changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
512 if (changedKeys == NULL) {
513 /* if no changes or something happened to the server */
514 return;
515 }
516
517 if (CFArrayGetCount(changedKeys) == 0) {
518 goto done;
519 }
520
521 rlsFunction = storePrivate->rlsFunction;
522
523 if (storePrivate->rlsContext.retain != NULL) {
524 context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info);
525 context_release = storePrivate->rlsContext.release;
526 } else {
527 context_info = storePrivate->rlsContext.info;
528 context_release = NULL;
529 }
530 (*rlsFunction)(store, changedKeys, context_info);
531 if (context_release) {
532 context_release(context_info);
533 }
534
535 done :
536
537 CFRelease(changedKeys);
538 return;
539 }
540
541
542 static CFTypeRef
543 rlsRetain(CFTypeRef cf)
544 {
545 SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
546 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
547
548 if (storePrivate->notifyStatus != Using_NotifierInformViaRunLoop) {
549 /* mark RLS active */
550 storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
551 /* keep a reference to the store */
552 CFRetain(store);
553 }
554
555 return cf;
556 }
557
558
559 static void
560 rlsRelease(CFTypeRef cf)
561 {
562 SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
563 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
564
565 /* mark RLS inactive */
566 storePrivate->notifyStatus = NotifierNotRegistered;
567 storePrivate->rls = NULL;
568
569 /* release our reference to the store */
570 CFRelease(store);
571
572 return;
573 }
574
575
576 static CFStringRef
577 rlsCopyDescription(const void *info)
578 {
579 CFMutableStringRef result;
580 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
581 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
582
583 result = CFStringCreateMutable(NULL, 0);
584 CFStringAppendFormat(result, NULL, CFSTR("<SCDynamicStore RLS> {"));
585 CFStringAppendFormat(result, NULL, CFSTR("store = %p"), store);
586 if (storePrivate->notifyStatus == Using_NotifierInformViaRunLoop) {
587 CFStringRef description = NULL;
588
589 CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->rlsFunction);
590
591 if ((storePrivate->rlsContext.info != NULL) && (storePrivate->rlsContext.copyDescription != NULL)) {
592 description = (*storePrivate->rlsContext.copyDescription)(storePrivate->rlsContext.info);
593 }
594 if (description == NULL) {
595 description = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCDynamicStore context %p>"), storePrivate->rlsContext.info);
596 }
597 if (description == NULL) {
598 description = CFRetain(CFSTR("<no description>"));
599 }
600 CFStringAppendFormat(result, NULL, CFSTR(", context = %@"), description);
601 CFRelease(description);
602 } else {
603 CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->callbackFunction);
604 CFStringAppendFormat(result, NULL, CFSTR(", info = %p"), storePrivate->callbackArgument);
605 }
606 CFStringAppendFormat(result, NULL, CFSTR("}"));
607
608 return result;
609 }
610
611
612 CFRunLoopSourceRef
613 SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator,
614 SCDynamicStoreRef store,
615 CFIndex order)
616 {
617 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
618
619 if (store == NULL) {
620 /* sorry, you must provide a session */
621 _SCErrorSet(kSCStatusNoStoreSession);
622 return NULL;
623 }
624
625 if (storePrivate->server == MACH_PORT_NULL) {
626 /* sorry, you must have an open session to play */
627 _SCErrorSet(kSCStatusNoStoreServer);
628 return NULL;
629 }
630
631 switch (storePrivate->notifyStatus) {
632 case NotifierNotRegistered :
633 case Using_NotifierInformViaRunLoop :
634 /* OK to enable runloop notification */
635 break;
636 default :
637 /* sorry, you can only have one notification registered at once */
638 _SCErrorSet(kSCStatusNotifierActive);
639 return NULL;
640 }
641
642 if (storePrivate->rls != NULL) {
643 CFRetain(storePrivate->rls);
644 } else {
645 CFRunLoopSourceContext context = { 0 // version
646 , (void *)store // info
647 , rlsRetain // retain
648 , rlsRelease // release
649 , rlsCopyDescription // copyDescription
650 , CFEqual // equal
651 , CFHash // hash
652 , rlsSchedule // schedule
653 , rlsCancel // cancel
654 , rlsPerform // perform
655 };
656
657 storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context);
658 if (storePrivate->rls == NULL) {
659 _SCErrorSet(kSCStatusFailed);
660 }
661 }
662
663 return storePrivate->rls;
664 }
665
666
667 #if !TARGET_OS_IPHONE
668 static boolean_t
669 SCDynamicStoreNotifyMIGCallback(mach_msg_header_t *message, mach_msg_header_t *reply)
670 {
671 SCDynamicStorePrivateRef storePrivate;
672
673 storePrivate = dispatch_get_context(dispatch_get_current_queue());
674 if (storePrivate != NULL) {
675 CFRetain(storePrivate);
676 dispatch_async(storePrivate->dispatchQueue, ^{
677 rlsPerform(storePrivate);
678 CFRelease(storePrivate);
679 });
680 }
681 reply->msgh_remote_port = MACH_PORT_NULL;
682 return false;
683 }
684
685
686 Boolean
687 SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store, dispatch_queue_t queue)
688 {
689 Boolean ok = FALSE;
690 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
691
692 if (store == NULL) {
693 /* sorry, you must provide a session */
694 _SCErrorSet(kSCStatusNoStoreSession);
695 return FALSE;
696 }
697
698 if (storePrivate->server == MACH_PORT_NULL) {
699 /* sorry, you must have an open session to play */
700 _SCErrorSet(kSCStatusNoStoreServer);
701 return FALSE;
702 }
703
704 if (queue != NULL) {
705 dispatch_queue_attr_t attr;
706 mach_port_t mp;
707 long res;
708
709 if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL)) {
710 _SCErrorSet(kSCStatusInvalidArgument);
711 return FALSE;
712 }
713
714 if (storePrivate->notifyStatus != NotifierNotRegistered) {
715 /* sorry, you can only have one notification registered at once... */
716 _SCErrorSet(kSCStatusNotifierActive);
717 return FALSE;
718 }
719
720 /*
721 * mark our using of the SCDynamicStore notifications, create and schedule
722 * the notification port (storePrivate->callbackPort), and a bunch of other
723 * "setup"
724 */
725 storePrivate->notifyStatus = Using_NotifierInformViaDispatch;
726 rlsSchedule((void*)store, NULL, NULL);
727 storePrivate->dispatchQueue = queue;
728 dispatch_retain(storePrivate->dispatchQueue);
729
730 /*
731 * create a queue for the mig source, we'll use this queue's context
732 * to carry the store pointer for the callback code.
733 */
734 attr = dispatch_queue_attr_create();
735 res = dispatch_queue_attr_set_finalizer(attr,
736 ^(dispatch_queue_t dq) {
737 SCDynamicStoreRef store;
738
739 store = (SCDynamicStoreRef)dispatch_get_context(dq);
740 CFRelease(store);
741 });
742 if (res != 0) {
743 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_queue_attr_set_finalizer() failed"));
744 dispatch_release(attr);
745 _SCErrorSet(kSCStatusFailed);
746 goto cleanup;
747 }
748 storePrivate->callbackQueue = dispatch_queue_create("com.apple.SCDynamicStore.notifications", attr);
749 dispatch_release(attr);
750 if (storePrivate->callbackQueue == NULL){
751 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_queue_create() failed"));
752 _SCErrorSet(kSCStatusFailed);
753 goto cleanup;
754 }
755 CFRetain(store); // Note: will be released when the dispatch queue is released
756 dispatch_set_context(storePrivate->callbackQueue, (void *)store);
757
758 dispatch_suspend(storePrivate->callbackQueue);
759 mp = CFMachPortGetPort(storePrivate->callbackPort);
760 storePrivate->callbackSource = dispatch_source_mig_create(mp, sizeof(mach_msg_header_t), NULL, storePrivate->callbackQueue, SCDynamicStoreNotifyMIGCallback);
761 if (storePrivate->callbackSource == NULL) {
762 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_source_mig_create() failed"));
763 _SCErrorSet(kSCStatusFailed);
764 goto cleanup;
765 }
766 dispatch_resume(storePrivate->callbackQueue);
767
768 ok = TRUE;
769 goto done;
770 } else {
771 if (storePrivate->dispatchQueue == NULL) {
772 _SCErrorSet(kSCStatusInvalidArgument);
773 return FALSE;
774 }
775
776 ok = TRUE;
777 }
778
779 cleanup :
780
781 if (storePrivate->callbackSource != NULL) {
782 dispatch_cancel(storePrivate->callbackSource);
783 dispatch_release(storePrivate->callbackSource);
784 storePrivate->callbackSource = NULL;
785 }
786 if (storePrivate->callbackQueue != NULL) {
787 dispatch_release(storePrivate->callbackQueue);
788 storePrivate->callbackQueue = NULL;
789 }
790 dispatch_release(storePrivate->dispatchQueue);
791 storePrivate->dispatchQueue = NULL;
792 rlsCancel((void*)store, NULL, NULL);
793 storePrivate->notifyStatus = NotifierNotRegistered;
794
795 done :
796
797 return ok;
798 }
799 #endif // !TARGET_OS_IPHONE