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