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