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