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