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