]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDOpen.c
configd-963.260.1.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCDOpen.c
1 /*
2 * Copyright (c) 2000-2006, 2008-2018 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 24, 2000 Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
34 #include <TargetConditionals.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 #include <mach/mach.h>
39 #include <mach/mach_error.h>
40 #include <servers/bootstrap.h>
41 #include <bootstrap_priv.h>
42
43 #include "SCDynamicStoreInternal.h"
44 #include "config.h" /* MiG generated file */
45 #include "SCD.h"
46
47 static CFStringRef _sc_bundleID = NULL;
48 static pthread_mutex_t _sc_lock = PTHREAD_MUTEX_INITIALIZER;
49 static mach_port_t _sc_server = MACH_PORT_NULL;
50
51
52 static const char *notifyType[] = {
53 "",
54 "wait",
55 "inform w/callback",
56 "inform w/mach port",
57 "inform w/fd",
58 "inform w/signal",
59 "inform w/runLoop",
60 "inform w/dispatch"
61 };
62
63
64 __private_extern__ os_log_t
65 __log_SCDynamicStore(void)
66 {
67 static os_log_t log = NULL;
68
69 if (log == NULL) {
70 log = os_log_create("com.apple.SystemConfiguration", "SCDynamicStore");
71 }
72
73 return log;
74 }
75
76
77 static CFStringRef
78 __SCDynamicStoreCopyDescription(CFTypeRef cf) {
79 CFAllocatorRef allocator = CFGetAllocator(cf);
80 CFMutableStringRef result;
81 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)cf;
82
83 result = CFStringCreateMutable(allocator, 0);
84 CFStringAppendFormat(result, NULL, CFSTR("<SCDynamicStore %p [%p]> {"), cf, allocator);
85 if (storePrivate->server != MACH_PORT_NULL) {
86 CFStringAppendFormat(result, NULL, CFSTR("server port = 0x%x"), storePrivate->server);
87 } else {
88 CFStringAppendFormat(result, NULL, CFSTR("server not (no longer) available"));
89 }
90 if (storePrivate->disconnectFunction != NULL) {
91 CFStringAppendFormat(result, NULL, CFSTR(", disconnect = %p"), storePrivate->disconnectFunction);
92 }
93 switch (storePrivate->notifyStatus) {
94 case Using_NotifierWait :
95 CFStringAppendFormat(result, NULL, CFSTR(", waiting for a notification"));
96 break;
97 case Using_NotifierInformViaMachPort :
98 CFStringAppendFormat(result, NULL, CFSTR(", mach port notifications"));
99 break;
100 case Using_NotifierInformViaFD :
101 CFStringAppendFormat(result, NULL, CFSTR(", FD notifications"));
102 break;
103 case Using_NotifierInformViaSignal :
104 CFStringAppendFormat(result, NULL, CFSTR(", BSD signal notifications"));
105 break;
106 case Using_NotifierInformViaRunLoop :
107 case Using_NotifierInformViaDispatch :
108 if (storePrivate->notifyStatus == Using_NotifierInformViaRunLoop) {
109 CFStringAppendFormat(result, NULL, CFSTR(", runloop notifications"));
110 CFStringAppendFormat(result, NULL, CFSTR(" {callout = %p"), storePrivate->rlsFunction);
111 CFStringAppendFormat(result, NULL, CFSTR(", info = %p"), storePrivate->rlsContext.info);
112 CFStringAppendFormat(result, NULL, CFSTR(", rls = %p"), storePrivate->rls);
113 } else if (storePrivate->notifyStatus == Using_NotifierInformViaDispatch) {
114 CFStringAppendFormat(result, NULL, CFSTR(", dispatch notifications"));
115 CFStringAppendFormat(result, NULL, CFSTR(" {callout = %p"), storePrivate->rlsFunction);
116 CFStringAppendFormat(result, NULL, CFSTR(", info = %p"), storePrivate->rlsContext.info);
117 CFStringAppendFormat(result, NULL, CFSTR(", queue = %p"), storePrivate->dispatchQueue);
118 CFStringAppendFormat(result, NULL, CFSTR(", source = %p"), storePrivate->dispatchSource);
119 }
120 if (storePrivate->rlsNotifyRLS != NULL) {
121 CFStringAppendFormat(result, NULL, CFSTR(", notify rls = %@" ), storePrivate->rlsNotifyRLS);
122 }
123 CFStringAppendFormat(result, NULL, CFSTR("}"));
124 break;
125 default :
126 CFStringAppendFormat(result, NULL, CFSTR(", notification delivery not requested%s"),
127 storePrivate->rlsFunction ? " (yet)" : "");
128 break;
129 }
130 CFStringAppendFormat(result, NULL, CFSTR("}"));
131
132 return result;
133 }
134
135
136 static void
137 __SCDynamicStoreDeallocate(CFTypeRef cf)
138 {
139 int oldThreadState;
140 SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
141 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
142
143 (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldThreadState);
144
145 /* Remove/cancel any outstanding notification requests. */
146 (void) SCDynamicStoreNotifyCancel(store);
147
148 if (storePrivate->server != MACH_PORT_NULL) {
149 /*
150 * Remove our send right to the SCDynamicStore server.
151 *
152 * In the case of a "real" session this will result in our
153 * session being closed.
154 *
155 * In the case of a "NULL" session, we just remove the
156 * the send right reference we are holding.
157 */
158 __MACH_PORT_DEBUG(TRUE, "*** __SCDynamicStoreDeallocate", storePrivate->server);
159 (void) mach_port_deallocate(mach_task_self(), storePrivate->server);
160 storePrivate->server = MACH_PORT_NULL;
161 }
162
163 (void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldThreadState);
164 pthread_testcancel();
165
166 /* release any callback context info */
167 if (storePrivate->rlsContext.release != NULL) {
168 (*storePrivate->rlsContext.release)(storePrivate->rlsContext.info);
169 }
170
171 /* release any keys being watched */
172 if (storePrivate->keys != NULL) CFRelease(storePrivate->keys);
173 if (storePrivate->patterns != NULL) CFRelease(storePrivate->patterns);
174
175 /* release any client info */
176 if (storePrivate->name != NULL) CFRelease(storePrivate->name);
177 if (storePrivate->options != NULL) CFRelease(storePrivate->options);
178
179 #ifdef VERBOSE_ACTIVITY_LOGGING
180 /* release activity tracing */
181 if (storePrivate->activity != NULL) os_release(storePrivate->activity);
182 #endif // VERBOSE_ACTIVITY_LOGGING
183
184 return;
185 }
186
187
188 static CFTypeID __kSCDynamicStoreTypeID = _kCFRuntimeNotATypeID;
189
190
191 static const CFRuntimeClass __SCDynamicStoreClass = {
192 0, // version
193 "SCDynamicStore", // className
194 NULL, // init
195 NULL, // copy
196 __SCDynamicStoreDeallocate, // dealloc
197 NULL, // equal
198 NULL, // hash
199 NULL, // copyFormattingDesc
200 __SCDynamicStoreCopyDescription // copyDebugDesc
201 };
202
203
204 static void
205 childForkHandler()
206 {
207 /* the process has forked (and we are the child process) */
208
209 _sc_server = MACH_PORT_NULL;
210 return;
211 }
212
213
214 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
215
216 static void
217 __SCDynamicStoreInitialize(void)
218 {
219 CFBundleRef bundle;
220
221 /* register with CoreFoundation */
222 __kSCDynamicStoreTypeID = _CFRuntimeRegisterClass(&__SCDynamicStoreClass);
223
224 /* add handler to cleanup after fork() */
225 (void) pthread_atfork(NULL, NULL, childForkHandler);
226
227 /* get the application/executable/bundle name */
228 bundle = CFBundleGetMainBundle();
229 if (bundle != NULL) {
230 _sc_bundleID = CFBundleGetIdentifier(bundle);
231 if (_sc_bundleID != NULL) {
232 CFRetain(_sc_bundleID);
233 } else {
234 CFURLRef url;
235
236 url = CFBundleCopyExecutableURL(bundle);
237 if (url != NULL) {
238 _sc_bundleID = CFURLCopyPath(url);
239 CFRelease(url);
240 }
241 }
242
243 if (_sc_bundleID != NULL) {
244 if (CFEqual(_sc_bundleID, CFSTR("/"))) {
245 CFRelease(_sc_bundleID);
246 _sc_bundleID = CFStringCreateWithFormat(NULL, NULL, CFSTR("(%d)"), getpid());
247 }
248 }
249 }
250
251 return;
252 }
253
254
255 static mach_port_t
256 __SCDynamicStoreServerPort(SCDynamicStorePrivateRef storePrivate, kern_return_t *status)
257 {
258 #pragma unused(storePrivate)
259 mach_port_t server = MACH_PORT_NULL;
260 char *server_name;
261
262 server_name = getenv("SCD_SERVER");
263
264 #ifndef DEBUG
265 /*
266 * only allow the SCDynamicStore server bootstrap name to be changed with
267 * DEBUG builds. For RELEASE builds, assume that no server is available.
268 */
269 if (server_name != NULL) {
270 *status = BOOTSTRAP_UNKNOWN_SERVICE;
271 return MACH_PORT_NULL;
272 }
273 #endif /* DEBUG */
274
275
276 if (server_name == NULL) {
277 server_name = SCD_SERVER;
278 }
279
280 #if defined(BOOTSTRAP_PRIVILEGED_SERVER) && !TARGET_OS_SIMULATOR
281 *status = bootstrap_look_up2(bootstrap_port,
282 server_name,
283 &server,
284 0,
285 BOOTSTRAP_PRIVILEGED_SERVER);
286 #else // defined(BOOTSTRAP_PRIVILEGED_SERVER) && !TARGET_OS_SIMULATOR
287 *status = bootstrap_look_up(bootstrap_port, server_name, &server);
288 #endif // defined(BOOTSTRAP_PRIVILEGED_SERVER) && !TARGET_OS_SIMULATOR
289
290 switch (*status) {
291 case BOOTSTRAP_SUCCESS :
292 /* service currently registered, "a good thing" (tm) */
293 return server;
294 case BOOTSTRAP_NOT_PRIVILEGED :
295 /* the service is not privileged */
296 break;
297 case BOOTSTRAP_UNKNOWN_SERVICE :
298 /* service not currently registered, try again later */
299 break;
300 default :
301 #ifdef DEBUG
302 SC_log(LOG_INFO, "bootstrap_look_up() failed: status=%s",
303 bootstrap_strerror(*status));
304 #endif /* DEBUG */
305 break;
306 }
307
308 return MACH_PORT_NULL;
309 }
310
311
312 SCDynamicStorePrivateRef
313 __SCDynamicStoreCreatePrivate(CFAllocatorRef allocator,
314 const CFStringRef name,
315 SCDynamicStoreCallBack callout,
316 SCDynamicStoreContext *context)
317 {
318 uint32_t size;
319 SCDynamicStorePrivateRef storePrivate;
320
321 /* initialize runtime */
322 pthread_once(&initialized, __SCDynamicStoreInitialize);
323
324 /* allocate session */
325 size = sizeof(SCDynamicStorePrivate) - sizeof(CFRuntimeBase);
326 storePrivate = (SCDynamicStorePrivateRef)_CFRuntimeCreateInstance(allocator,
327 __kSCDynamicStoreTypeID,
328 size,
329 NULL);
330 if (storePrivate == NULL) {
331 _SCErrorSet(kSCStatusFailed);
332 return NULL;
333 }
334
335 /* initialize non-zero/NULL members */
336
337 /* client side of the "configd" session */
338 storePrivate->name = (name != NULL) ? CFRetain(name) : NULL;
339
340 #ifdef VERBOSE_ACTIVITY_LOGGING
341 /* "client" activity tracing */
342 storePrivate->activity = os_activity_create("accessing SCDynamicStore",
343 OS_ACTIVITY_CURRENT,
344 OS_ACTIVITY_FLAG_DEFAULT);
345 #endif // VERBOSE_ACTIVITY_LOGGING
346
347 /* Notification status */
348 storePrivate->notifyStatus = NotifierNotRegistered;
349
350 /* "client" information associated with SCDynamicStoreCreateRunLoopSource() */
351 storePrivate->rlsFunction = callout;
352 if (context != NULL) {
353 bcopy(context, &storePrivate->rlsContext, sizeof(SCDynamicStoreContext));
354 if (context->retain != NULL) {
355 storePrivate->rlsContext.info = (void *)(*context->retain)(context->info);
356 }
357 }
358
359 /* "server" information associated with SCDynamicStoreNotifyFileDescriptor(); */
360 storePrivate->notifyFile = -1;
361
362 return storePrivate;
363 }
364
365
366 static void
367 updateServerPort(SCDynamicStorePrivateRef storePrivate, mach_port_t *server, int *sc_status_p)
368 {
369 pthread_mutex_lock(&_sc_lock);
370 if (_sc_server != MACH_PORT_NULL) {
371 if (*server == _sc_server) {
372 mach_port_t old_port;
373
374 // if the server we tried returned the error, save the old port,
375 // [re-]lookup the name to the server, and deallocate the original
376 // send [or dead name] right
377
378 old_port = _sc_server;
379 _sc_server = __SCDynamicStoreServerPort(storePrivate, sc_status_p);
380 (void)mach_port_deallocate(mach_task_self(), old_port);
381 } else {
382 // another thread has refreshed the [main] SCDynamicStore server port
383 }
384 } else {
385 _sc_server = __SCDynamicStoreServerPort(storePrivate, sc_status_p);
386 }
387 *server = _sc_server;
388 pthread_mutex_unlock(&_sc_lock);
389
390 return;
391 }
392
393
394 static Boolean
395 __SCDynamicStoreAddSession(SCDynamicStorePrivateRef storePrivate)
396 {
397 #ifdef VERBOSE_ACTIVITY_LOGGING
398 struct os_activity_scope_state_s activity_state;
399 #endif // VERBOSE_ACTIVITY_LOGGING
400 kern_return_t kr = KERN_SUCCESS;
401 CFDataRef myName; /* serialized name */
402 xmlData_t myNameRef;
403 CFIndex myNameLen;
404 CFDataRef myOptions = NULL; /* serialized options */
405 xmlData_t myOptionsRef = NULL;
406 CFIndex myOptionsLen = 0;
407 int sc_status = kSCStatusFailed;
408 mach_port_t server;
409
410 if (!_SCSerializeString(storePrivate->name, &myName, (void **)&myNameRef, &myNameLen)) {
411 goto done;
412 }
413
414 /* serialize the options */
415 if (storePrivate->options != NULL) {
416 if (!_SCSerialize(storePrivate->options, &myOptions, (void **)&myOptionsRef, &myOptionsLen)) {
417 CFRelease(myName);
418 goto done;
419 }
420 }
421
422 /* open a new session with the server */
423 server = MACH_PORT_NULL;
424
425
426 updateServerPort(storePrivate, &server, &sc_status);
427
428
429 #ifdef VERBOSE_ACTIVITY_LOGGING
430 os_activity_scope_enter(storePrivate->activity, &activity_state);
431 #endif // VERBOSE_ACTIVITY_LOGGING
432
433 while (server != MACH_PORT_NULL) {
434 // if SCDynamicStore server available
435
436 if (!storePrivate->serverNullSession) {
437 // if SCDynamicStore session
438 kr = configopen(server,
439 myNameRef,
440 (mach_msg_type_number_t)myNameLen,
441 myOptionsRef,
442 (mach_msg_type_number_t)myOptionsLen,
443 &storePrivate->server,
444 (int *)&sc_status);
445 } else {
446 // if NULL session
447 if (storePrivate->server == MACH_PORT_NULL) {
448 // use the [main] SCDynamicStore server port
449 kr = mach_port_mod_refs(mach_task_self(), server, MACH_PORT_RIGHT_SEND, +1);
450 if (kr == KERN_SUCCESS) {
451 storePrivate->server = server;
452 sc_status = kSCStatusOK;
453 } else {
454 if (kr == KERN_INVALID_RIGHT) {
455 // We can get KERN_INVALID_RIGHT if the server dies and we try to
456 // add a send right to a stale (now dead) port name
457 kr = MACH_SEND_INVALID_DEST;
458 }
459 storePrivate->server = MACH_PORT_NULL;
460 }
461 } else {
462 // if the server port we used returned an error
463 storePrivate->server = MACH_PORT_NULL;
464 kr = MACH_SEND_INVALID_DEST;
465 }
466 }
467
468 if (kr == KERN_SUCCESS) {
469 break;
470 }
471
472 // our [cached] server port is not valid
473 if ((kr != MACH_SEND_INVALID_DEST) && (kr != MIG_SERVER_DIED)) {
474 // if we got an unexpected error, don't retry
475 sc_status = kr;
476 break;
477 }
478
479
480 updateServerPort(storePrivate, &server, &sc_status);
481 }
482 __MACH_PORT_DEBUG(TRUE, "*** SCDynamicStoreAddSession", storePrivate->server);
483
484 #ifdef VERBOSE_ACTIVITY_LOGGING
485 os_activity_scope_leave(&activity_state);
486 #endif // VERBOSE_ACTIVITY_LOGGING
487
488 // clean up
489 CFRelease(myName);
490 if (myOptions != NULL) CFRelease(myOptions);
491
492 done :
493
494 switch (sc_status) {
495 case kSCStatusOK :
496 return TRUE;
497 case BOOTSTRAP_UNKNOWN_SERVICE :
498 SC_log((kr == KERN_SUCCESS) ? LOG_INFO : LOG_ERR, "SCDynamicStore server not available");
499 sc_status = kSCStatusNoStoreServer;
500 break;
501 default :
502 SC_log((kr == KERN_SUCCESS) ? LOG_INFO : LOG_ERR, "configopen() failed: %d: %s",
503 sc_status,
504 SCErrorString(sc_status));
505 break;
506 }
507
508 _SCErrorSet(sc_status);
509 return FALSE;
510 }
511
512
513 __private_extern__
514 SCDynamicStoreRef
515 __SCDynamicStoreNullSession(void)
516 {
517 SCDynamicStorePrivateRef storePrivate;
518 Boolean ok = TRUE;
519 __SCThreadSpecificDataRef tsd;
520
521 tsd = __SCGetThreadSpecificData();
522 if (tsd->_sc_store == NULL) {
523 storePrivate = __SCDynamicStoreCreatePrivate(NULL,
524 CFSTR("NULL session"),
525 NULL,
526 NULL);
527 assert(storePrivate != NULL);
528 storePrivate->serverNullSession = TRUE;
529 tsd->_sc_store = (SCDynamicStoreRef)storePrivate;
530 }
531
532 storePrivate = (SCDynamicStorePrivateRef)tsd->_sc_store;
533 if (storePrivate->server == MACH_PORT_NULL) {
534 ok = __SCDynamicStoreAddSession(storePrivate);
535 }
536
537 return ok ? tsd->_sc_store : NULL;
538 }
539
540
541 static Boolean
542 __SCDynamicStoreReconnect(SCDynamicStoreRef store)
543 {
544 Boolean ok;
545 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
546
547 ok = __SCDynamicStoreAddSession(storePrivate);
548 return ok;
549 }
550
551
552 __private_extern__
553 Boolean
554 __SCDynamicStoreCheckRetryAndHandleError(SCDynamicStoreRef store,
555 kern_return_t status,
556 int *sc_status,
557 const char *log_str)
558 {
559 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
560
561 if (status == KERN_SUCCESS) {
562 /* no error */
563 return FALSE;
564 }
565
566 if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
567 /* the server's gone, remove the session's dead name right */
568 (void) mach_port_deallocate(mach_task_self(), storePrivate->server);
569 storePrivate->server = MACH_PORT_NULL;
570
571 /* reconnect */
572 if (__SCDynamicStoreReconnect(store)) {
573 /* retry needed */
574 return TRUE;
575 } else {
576 status = SCError();
577 }
578 } else {
579 /* an unexpected error, leave the [session] port alone */
580 SC_log(LOG_NOTICE, "%s: %s", log_str, mach_error_string(status));
581 storePrivate->server = MACH_PORT_NULL;
582 }
583
584 *sc_status = status;
585 return FALSE;
586 }
587
588
589 static void
590 pushDisconnect(SCDynamicStoreRef store)
591 {
592 void *context_info;
593 void (*context_release)(const void *);
594 SCDynamicStoreDisconnectCallBack disconnectFunction;
595 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
596
597 disconnectFunction = storePrivate->disconnectFunction;
598 if (disconnectFunction == NULL) {
599 // if no reconnect callout, push empty notification
600 storePrivate->disconnectForceCallBack = TRUE;
601 return;
602 }
603
604 if (storePrivate->rlsContext.retain != NULL) {
605 context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info);
606 context_release = storePrivate->rlsContext.release;
607 } else {
608 context_info = storePrivate->rlsContext.info;
609 context_release = NULL;
610 }
611 SC_log(LOG_DEBUG, "exec SCDynamicStore disconnect callout");
612 (*disconnectFunction)(store, context_info);
613 if (context_release) {
614 context_release(context_info);
615 }
616
617 return;
618 }
619
620
621 __private_extern__
622 Boolean
623 __SCDynamicStoreReconnectNotifications(SCDynamicStoreRef store)
624 {
625 dispatch_queue_t dispatchQueue = NULL;
626 __SCDynamicStoreNotificationStatus notifyStatus;
627 Boolean ok = TRUE;
628 CFArrayRef rlList = NULL;
629 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
630
631 // save old SCDynamicStore [notification] state
632 notifyStatus = storePrivate->notifyStatus;
633
634 // before tearing down our [old] notifications, make sure we've
635 // retained any information that will be lost when we cancel the
636 // current no-longer-valid handler
637 switch (notifyStatus) {
638 case Using_NotifierInformViaRunLoop :
639 if (storePrivate->rlList != NULL) {
640 rlList = CFArrayCreateCopy(NULL, storePrivate->rlList);
641 }
642 break;
643 case Using_NotifierInformViaDispatch :
644 dispatchQueue = storePrivate->dispatchQueue;
645 if (dispatchQueue != NULL) dispatch_retain(dispatchQueue);
646 break;
647 default :
648 break;
649 }
650
651 // cancel [old] notifications
652 if (!SCDynamicStoreNotifyCancel(store)) {
653 // if we could not cancel / reconnect
654 SC_log(LOG_NOTICE, "SCDynamicStoreNotifyCancel() failed: %s", SCErrorString(SCError()));
655 }
656
657 // set notification keys & patterns
658 if ((storePrivate->keys != NULL) || (storePrivate->patterns)) {
659 ok = SCDynamicStoreSetNotificationKeys(store,
660 storePrivate->keys,
661 storePrivate->patterns);
662 if (!ok) {
663 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE) {
664 SC_log(LOG_NOTICE, "SCDynamicStoreSetNotificationKeys() failed");
665 }
666 goto done;
667 }
668 }
669
670 switch (notifyStatus) {
671 case Using_NotifierInformViaRunLoop : {
672 CFIndex i;
673 CFIndex n;
674 CFRunLoopSourceRef rls;
675
676 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
677 if (rls == NULL) {
678 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE) {
679 SC_log(LOG_NOTICE, "SCDynamicStoreCreateRunLoopSource() failed");
680 }
681 ok = FALSE;
682 break;
683 }
684
685 n = (rlList != NULL) ? CFArrayGetCount(rlList) : 0;
686 for (i = 0; i < n; i += 3) {
687 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1);
688 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(rlList, i+2);
689
690 CFRunLoopAddSource(rl, rls, rlMode);
691 }
692
693 CFRelease(rls);
694 break;
695 }
696 case Using_NotifierInformViaDispatch :
697 ok = SCDynamicStoreSetDispatchQueue(store, dispatchQueue);
698 if (!ok) {
699 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE) {
700 SC_log(LOG_NOTICE, "SCDynamicStoreSetDispatchQueue() failed");
701 }
702 goto done;
703 }
704 break;
705
706 default :
707 _SCErrorSet(kSCStatusFailed);
708 ok = FALSE;
709 break;
710 }
711
712 done :
713
714 // cleanup
715 switch (notifyStatus) {
716 case Using_NotifierInformViaRunLoop :
717 if (rlList != NULL) CFRelease(rlList);
718 break;
719 case Using_NotifierInformViaDispatch :
720 if (dispatchQueue != NULL) dispatch_release(dispatchQueue);
721 break;
722 default :
723 break;
724 }
725
726 if (!ok) {
727 SC_log(LOG_NOTICE, "SCDynamicStore server %s, notification (%s) not restored",
728 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE) ? "shutdown" : "failed",
729 notifyType[notifyStatus]);
730 }
731
732 // inform the client
733 pushDisconnect(store);
734
735 return ok;
736 }
737
738
739 const CFStringRef kSCDynamicStoreUseSessionKeys = CFSTR("UseSessionKeys"); /* CFBoolean */
740
741
742
743 SCDynamicStoreRef
744 SCDynamicStoreCreateWithOptions(CFAllocatorRef allocator,
745 CFStringRef name,
746 CFDictionaryRef storeOptions,
747 SCDynamicStoreCallBack callout,
748 SCDynamicStoreContext *context)
749 {
750 Boolean ok;
751 SCDynamicStorePrivateRef storePrivate;
752
753 // allocate and initialize a new session
754 storePrivate = __SCDynamicStoreCreatePrivate(allocator, NULL, callout, context);
755 if (storePrivate == NULL) {
756 return NULL;
757 }
758
759 // set "name"
760 if (_sc_bundleID != NULL) {
761 storePrivate->name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@:%@"), _sc_bundleID, name);
762 } else {
763 storePrivate->name = CFRetain(name);
764 }
765
766 // set "options"
767
768 if (storeOptions != NULL) {
769 storePrivate->options = CFRetain(storeOptions);
770 }
771
772 // establish SCDynamicStore session
773 ok = __SCDynamicStoreAddSession(storePrivate);
774 if (!ok) {
775 CFRelease(storePrivate);
776 storePrivate = NULL;
777 }
778
779 return (SCDynamicStoreRef)storePrivate;
780 }
781
782
783 SCDynamicStoreRef
784 SCDynamicStoreCreate(CFAllocatorRef allocator,
785 CFStringRef name,
786 SCDynamicStoreCallBack callout,
787 SCDynamicStoreContext *context)
788 {
789 return SCDynamicStoreCreateWithOptions(allocator, name, NULL, callout, context);
790 }
791
792
793 CFTypeID
794 SCDynamicStoreGetTypeID(void) {
795 pthread_once(&initialized, __SCDynamicStoreInitialize); /* initialize runtime */
796 return __kSCDynamicStoreTypeID;
797 }
798
799
800 Boolean
801 SCDynamicStoreSetDisconnectCallBack(SCDynamicStoreRef store,
802 SCDynamicStoreDisconnectCallBack callout)
803 {
804 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
805
806 if (store == NULL) {
807 /* sorry, you must provide a session */
808 _SCErrorSet(kSCStatusNoStoreSession);
809 return FALSE;
810 }
811
812 storePrivate->disconnectFunction = callout;
813 return TRUE;
814 }