2 * Copyright (c) 2000-2008, 2010, 2012-2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * April 2, 2004 Allan Nathanson <ajn@apple.com>
28 * - use SCPreference notification APIs
30 * June 24, 2001 Allan Nathanson <ajn@apple.com>
31 * - update to public SystemConfiguration.framework APIs
33 * November 10, 2000 Allan Nathanson <ajn@apple.com>
38 #include <TargetConditionals.h>
41 #include <sys/types.h>
46 #define SC_LOG_HANDLE __log_PreferencesMonitor()
47 #define SC_LOG_HANDLE_TYPE static
48 #include <SystemConfiguration/SystemConfiguration.h>
49 #include <SystemConfiguration/SCPrivate.h>
50 #include <SystemConfiguration/SCValidation.h>
51 #include "plugin_shared.h"
54 #include <CommonCrypto/CommonDigest.h>
58 static SCPreferencesRef prefs
= NULL
;
59 static SCDynamicStoreRef store
= NULL
;
61 /* InterfaceNamer[.plugin] monitoring globals */
62 Boolean haveConfiguration
= FALSE
;
63 static CFStringRef namerKey
= NULL
;
64 static CFMutableArrayRef preconfigured_names
= NULL
; // of CFStringRef (BSD name)
65 static CFMutableArrayRef preconfigured_interfaces
= NULL
; // of SCNetworkInterfaceRef
67 /* KernelEventMonitor[.plugin] monitoring globals */
68 static CFStringRef interfacesKey
= NULL
;
70 /* SCDynamicStore (Setup:) */
71 static CFMutableDictionaryRef currentPrefs
; /* current prefs */
72 static CFMutableDictionaryRef newPrefs
; /* new prefs */
73 static CFMutableArrayRef unchangedPrefsKeys
; /* new prefs keys which match current */
74 static CFMutableArrayRef removedPrefsKeys
; /* old prefs keys to be removed */
76 static Boolean rofs
= FALSE
;
77 static Boolean restorePrefs
= FALSE
;
79 #define MY_PLUGIN_NAME "PreferencesMonitor"
80 #define MY_PLUGIN_ID CFSTR("com.apple.SystemConfiguration." MY_PLUGIN_NAME)
84 updateConfiguration(SCPreferencesRef prefs
,
85 SCPreferencesNotification notificationType
,
90 __log_PreferencesMonitor()
92 static os_log_t log
= NULL
;
95 log
= os_log_create("com.apple.SystemConfiguration", "PreferencesMonitor");
106 CFStringRef currentModel
= NULL
;
107 CFMutableStringRef modelPrefixStr
= NULL
;
108 CFArrayRef keyList
= NULL
;
109 CFIndex keyListCount
;
111 Boolean modified
= FALSE
;
112 int sc_status
= kSCStatusFailed
;
115 ok
= SCPreferencesLock(prefs
, TRUE
);
120 sc_status
= SCError();
121 if (sc_status
== kSCStatusStale
) {
122 SCPreferencesSynchronize(prefs
);
124 SC_log(LOG_NOTICE
, "Could not acquire network configuration lock: %s",
125 SCErrorString(sc_status
));
130 keyList
= SCPreferencesCopyKeyList(prefs
);
131 if (keyList
== NULL
) {
135 currentModel
= _SC_hw_model(FALSE
);
136 if (currentModel
== NULL
) {
140 /* Create "model:" string for prefix-check */
141 modelPrefixStr
= CFStringCreateMutableCopy(NULL
, 0, currentModel
);
142 CFStringAppend(modelPrefixStr
, CFSTR(":"));
144 keyListCount
= CFArrayGetCount(keyList
);
145 for (idx
= 0; idx
< keyListCount
; idx
++) {
146 CFStringRef existingKey
= CFArrayGetValueAtIndex(keyList
, idx
);
148 CFArrayRef splitKey
= NULL
;
149 CFPropertyListRef value
;
151 if (isA_CFString(existingKey
) == NULL
) {
155 if (!CFStringHasPrefix(existingKey
, modelPrefixStr
)) {
159 splitKey
= CFStringCreateArrayBySeparatingStrings(NULL
, existingKey
, CFSTR(":"));
160 key
= CFArrayGetValueAtIndex(splitKey
, 1);
161 value
= SCPreferencesGetValue(prefs
, existingKey
);
162 SCPreferencesSetValue(prefs
, key
, value
);
163 SCPreferencesRemoveValue(prefs
, existingKey
);
169 SCPreferencesRef ni_prefs
= NULL
;
170 ni_prefs
= SCPreferencesCreate(NULL
, MY_PLUGIN_ID
, CFSTR("NetworkInterfaces.plist"));
171 if (ni_prefs
== NULL
) {
175 ok
= _SCNetworkConfigurationCheckValidityWithPreferences(prefs
, ni_prefs
, NULL
);
178 //Commit the changes only if prefs files valid
180 if (!SCPreferencesCommitChanges(prefs
)) {
181 if (SCError() != EROFS
) {
182 SC_log(LOG_NOTICE
, "SCPreferencesCommitChanges() failed: %s",
183 SCErrorString(SCError()));
189 (void) SCPreferencesApplyChanges(prefs
);
194 (void) SCPreferencesUnlock(prefs
);
196 if (keyList
!= NULL
) {
199 if (modelPrefixStr
!= NULL
) {
200 CFRelease(modelPrefixStr
);
207 establishNewPreferences()
209 SCNetworkSetRef current
= NULL
;
210 CFStringRef new_model
;
212 int sc_status
= kSCStatusFailed
;
213 SCNetworkSetRef set
= NULL
;
214 Boolean updated
= FALSE
;
217 ok
= SCPreferencesLock(prefs
, TRUE
);
222 sc_status
= SCError();
223 if (sc_status
== kSCStatusStale
) {
224 SCPreferencesSynchronize(prefs
);
226 SC_log(LOG_NOTICE
, "Could not acquire network configuration lock: %s",
227 SCErrorString(sc_status
));
232 /* Ensure that the preferences has the new model */
233 new_model
= _SC_hw_model(FALSE
);
235 /* Need to regenerate the new configuration for new model */
236 if (new_model
!= NULL
) {
237 CFStringRef old_model
;
239 old_model
= SCPreferencesGetValue(prefs
, MODEL
);
240 if ((old_model
!= NULL
) && !_SC_CFEqual(old_model
, new_model
)) {
245 keys
= SCPreferencesCopyKeyList(prefs
);
246 count
= (keys
!= NULL
) ? CFArrayGetCount(keys
) : 0;
248 for (index
= 0; index
< count
; index
++) {
249 CFStringRef existing_key
;
251 existing_key
= CFArrayGetValueAtIndex(keys
, index
);
252 if (isA_CFString(existing_key
) != NULL
) {
254 CFPropertyListRef value
;
256 /* If it already contains a Model
257 or if it already contains a MODEL:KEY key skip it*/
258 if (CFEqual(existing_key
, MODEL
)
259 || CFStringFind(existing_key
, CFSTR(":"), 0).location
264 value
= SCPreferencesGetValue(prefs
, existing_key
);
266 /* Create a new key as OLD_MODEL:OLD_KEY */
267 new_key
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@:%@"),
268 old_model
, existing_key
);
269 SCPreferencesSetValue(prefs
, new_key
, value
);
270 if (!CFEqual(existing_key
, kSCPrefSystem
)) {
271 /* preserve existing host names */
272 SCPreferencesRemoveValue(prefs
, existing_key
);
282 /* Set the new model */
283 SCPreferencesSetValue(prefs
, MODEL
, new_model
);
286 current
= SCNetworkSetCopyCurrent(prefs
);
287 if (current
!= NULL
) {
292 set
= _SCNetworkSetCreateDefault(prefs
);
295 sc_status
= SCError();
300 ok
= SCNetworkSetEstablishDefaultConfiguration(set
);
302 sc_status
= SCError();
309 ok
= SCPreferencesCommitChanges(prefs
);
311 SC_log(LOG_NOTICE
, "New network configuration saved");
314 sc_status
= SCError();
315 if (sc_status
== EROFS
) {
316 /* a read-only fileysstem is OK */
319 /* ... but we don't want to synchronize */
324 /* apply (committed or temporary/read-only) changes */
325 (void) SCPreferencesApplyChanges(prefs
);
326 } else if ((current
== NULL
) && (set
!= NULL
)) {
327 (void) SCNetworkSetRemove(set
);
331 if (sc_status
== kSCStatusOK
) {
332 SC_log(LOG_NOTICE
, "Network configuration not updated");
334 SC_log(LOG_NOTICE
, "Could not establish network configuration: %s",
335 SCErrorString(sc_status
));
339 (void)SCPreferencesUnlock(prefs
);
340 if (set
!= NULL
) CFRelease(set
);
346 watchSCDynamicStore()
348 CFMutableArrayRef keys
;
350 CFRunLoopSourceRef rls
;
353 * watch for KernelEventMonitor[.bundle] changes (the list of
354 * active network interfaces)
356 interfacesKey
= SCDynamicStoreKeyCreateNetworkInterface(NULL
,
357 kSCDynamicStoreDomainState
);
360 * watch for InterfaceNamer[.bundle] changes (quiet, timeout,
361 * and the list of pre-configured interfaces)
363 namerKey
= SCDynamicStoreKeyCreate(NULL
,
364 CFSTR("%@" "InterfaceNamer"),
365 kSCDynamicStoreDomainPlugin
);
367 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
369 SC_log(LOG_NOTICE
, "SCDynamicStoreCreateRunLoopSource() failed: %s", SCErrorString(SCError()));
370 haveConfiguration
= TRUE
;
373 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
376 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
377 CFArrayAppendValue(keys
, interfacesKey
);
378 CFArrayAppendValue(keys
, namerKey
);
379 ok
= SCDynamicStoreSetNotificationKeys(store
, keys
, NULL
);
382 SC_log(LOG_NOTICE
, "SCDynamicStoreSetNotificationKeys() failed: %s", SCErrorString(SCError()));
383 haveConfiguration
= TRUE
;
391 previousConfigurationAvailable()
393 CFStringRef backupKey
= NULL
;
394 CFStringRef currentModel
= NULL
;
395 CFPropertyListRef properties
= NULL
;
397 currentModel
= _SC_hw_model(FALSE
);
398 if (currentModel
== NULL
) {
402 /* Currently relying only if a backup of "Sets" is present */
403 backupKey
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@:Sets"), currentModel
);
404 properties
= SCPreferencesGetValue(prefs
, backupKey
);
405 CFRelease(backupKey
);
407 return (properties
!= NULL
);
412 storeCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
415 CFDictionaryRef dict
;
416 Boolean quiet
= FALSE
;
417 Boolean timeout
= FALSE
;
418 Boolean updated
= FALSE
;
421 * Capture/process InterfaceNamer[.bundle] info
422 * 1. check if IORegistry "quiet", "timeout"
423 * 2. update list of named pre-configured interfaces
425 dict
= SCDynamicStoreCopyValue(store
, namerKey
);
427 if (isA_CFDictionary(dict
)) {
429 CFArrayRef preconfigured
;
431 if (CFDictionaryContainsKey(dict
, kInterfaceNamerKey_Quiet
)) {
434 if (CFDictionaryContainsKey(dict
, kInterfaceNamerKey_Timeout
)) {
438 preconfigured
= CFDictionaryGetValue(dict
, kInterfaceNamerKey_PreConfiguredInterfaces
);
439 preconfigured
= isA_CFArray(preconfigured
);
441 n
= (preconfigured
!= NULL
) ? CFArrayGetCount(preconfigured
) : 0;
442 for (CFIndex i
= 0; i
< n
; i
++) {
443 CFStringRef bsdName
= CFArrayGetValueAtIndex(preconfigured
, i
);
444 SCNetworkInterfaceRef interface
;
448 range
.length
= (preconfigured_names
!= NULL
) ? CFArrayGetCount(preconfigured_names
) : 0;
449 if ((range
.length
> 0) &&
450 CFArrayContainsValue(preconfigured_names
, range
, bsdName
)) {
451 // if we already know about this interface
455 for (int retry
= 0; retry
< 10; retry
++) {
457 // add short delay (before retry)
458 usleep(20 * 1000); // 20ms
461 interface
= _SCNetworkInterfaceCreateWithBSDName(NULL
, bsdName
, kIncludeNoVirtualInterfaces
);
462 if (interface
== NULL
) {
463 SC_log(LOG_ERR
, "could not create network interface for %@", bsdName
);
464 } else if (_SCNetworkInterfaceGetIOPath(interface
) == NULL
) {
465 SC_log(LOG_ERR
, "could not get IOPath for %@", bsdName
);
466 CFRelease(interface
);
470 if (interface
!= NULL
) {
471 // if we have an interface
476 if (interface
== NULL
) {
477 // if SCNetworkInterface not [currently] available
481 // keep track of the interface name (quicker than having to iterate the list
482 // of SCNetworkInterfaces, extract the name, and compare).
483 if (preconfigured_names
== NULL
) {
484 preconfigured_names
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
486 CFArrayAppendValue(preconfigured_names
, bsdName
);
488 if (preconfigured_interfaces
== NULL
) {
489 preconfigured_interfaces
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
491 CFArrayAppendValue(preconfigured_interfaces
, interface
);
492 CFRelease(interface
);
498 CFStringRef interfaces
= CFSTR("<empty>");
500 // report [new] pre-configured interfaces
501 if (preconfigured_names
!= NULL
) {
502 interfaces
= CFStringCreateByCombiningStrings(NULL
, preconfigured_names
, CFSTR(","));
504 CFRetain(interfaces
);
506 SC_log(LOG_INFO
, "pre-configured interface list changed: %@", interfaces
);
507 CFRelease(interfaces
);
514 if (!haveConfiguration
&& (quiet
|| timeout
)) {
515 static int logged
= 0;
518 #if !TARGET_OS_IPHONE
520 #endif /* !TARGET_OS_IPHONE */
522 haveConfiguration
= TRUE
;
525 (void) establishNewPreferences();
528 (void) restorePreferences();
529 restorePrefs
= FALSE
;
532 if (timeout
&& (logged
++ == 0)) {
533 SC_log(LOG_ERR
, "Network configuration creation timed out waiting for IORegistry");
537 if (updated
&& (changedKeys
!= NULL
)) {
538 // if pre-configured interface list changed
539 updateConfiguration(prefs
, kSCPreferencesNotificationApply
, (void *)store
);
547 updateCache(const void *key
, const void *value
, void *context
)
549 #pragma unused(context)
550 CFStringRef configKey
= (CFStringRef
)key
;
551 CFPropertyListRef configData
= (CFPropertyListRef
)value
;
552 CFPropertyListRef cacheData
;
555 cacheData
= CFDictionaryGetValue(currentPrefs
, configKey
);
558 if (CFEqual(cacheData
, configData
)) {
560 * if the old & new property list values have
561 * not changed then we don't need to update
564 CFArrayAppendValue(unchangedPrefsKeys
, configKey
);
568 /* in any case, this key should not be removed */
569 i
= CFArrayGetFirstIndexOfValue(removedPrefsKeys
,
570 CFRangeMake(0, CFArrayGetCount(removedPrefsKeys
)),
572 if (i
!= kCFNotFound
) {
573 CFArrayRemoveValueAtIndex(removedPrefsKeys
, i
);
581 flatten(SCPreferencesRef prefs
,
583 CFDictionaryRef base
)
585 CFDictionaryRef subset
;
587 CFMutableDictionaryRef myDict
;
594 if (!CFDictionaryGetValueIfPresent(base
, kSCResvLink
, (const void **)&link
)) {
595 /* if this dictionary is not linked */
598 /* if __LINK__ key is present */
599 subset
= SCPreferencesPathGetValue(prefs
, link
);
601 /* if error with link */
602 SC_log(LOG_NOTICE
, "SCPreferencesPathGetValue(,%@,) failed: %s",
604 SCErrorString(SCError()));
609 if (CFDictionaryContainsKey(subset
, kSCResvInactive
)) {
610 /* if __INACTIVE__ key is present */
614 myKey
= CFStringCreateWithFormat(NULL
,
617 kSCDynamicStoreDomainSetup
,
620 myDict
= (CFMutableDictionaryRef
)CFDictionaryGetValue(newPrefs
, myKey
);
622 myDict
= CFDictionaryCreateMutableCopy(NULL
,
624 (CFDictionaryRef
)myDict
);
626 myDict
= CFDictionaryCreateMutable(NULL
,
628 &kCFTypeDictionaryKeyCallBacks
,
629 &kCFTypeDictionaryValueCallBacks
);
632 nKeys
= CFDictionaryGetCount(subset
);
634 keys
= CFAllocatorAllocate(NULL
, nKeys
* sizeof(CFStringRef
) , 0);
635 vals
= CFAllocatorAllocate(NULL
, nKeys
* sizeof(CFPropertyListRef
), 0);
636 CFDictionaryGetKeysAndValues(subset
, keys
, vals
);
637 for (i
= 0; i
< nKeys
; i
++) {
638 if (CFGetTypeID((CFTypeRef
)vals
[i
]) != CFDictionaryGetTypeID()) {
639 /* add this key/value to the current dictionary */
640 CFDictionarySetValue(myDict
, keys
[i
], vals
[i
]);
644 /* flatten [sub]dictionaries */
645 subKey
= CFStringCreateWithFormat(NULL
,
649 CFEqual(key
, CFSTR("/")) ? "" : "/",
651 flatten(prefs
, subKey
, vals
[i
]);
655 CFAllocatorDeallocate(NULL
, keys
);
656 CFAllocatorDeallocate(NULL
, vals
);
659 if (CFDictionaryGetCount(myDict
) > 0) {
660 /* add this dictionary to the new preferences */
661 CFDictionarySetValue(newPrefs
, myKey
, myDict
);
671 static CF_RETURNS_RETAINED SCNetworkServiceRef
672 copyInterfaceService(SCNetworkSetRef set
, CFStringRef matchName
)
676 SCNetworkServiceRef service
= NULL
;
679 services
= SCNetworkSetCopyServices(set
);
680 assert(services
!= NULL
);
682 n
= CFArrayGetCount(services
);
683 for (i
= 0; i
< n
; i
++) {
684 SCNetworkInterfaceRef interface
;
686 service
= CFArrayGetValueAtIndex(services
, i
);
687 interface
= SCNetworkServiceGetInterface(service
);
688 if (interface
!= NULL
) {
691 bsdName
= SCNetworkInterfaceGetBSDName(interface
);
692 if (_SC_CFEqual(bsdName
, matchName
)) {
707 static CF_RETURNS_RETAINED CFStringRef
708 copyInterfaceUUID(CFStringRef bsdName
)
711 unsigned char sha1_bytes
[CC_SHA1_DIGEST_LENGTH
];
712 CFUUIDBytes uuid_bytes
;
715 char if_name
[IF_NAMESIZE
];
717 CFStringRef uuid_str
;
719 // start with interface name
720 bzero(&if_name
, sizeof(if_name
));
721 (void) _SC_cfstring_to_cstring(bsdName
,
724 kCFStringEncodingASCII
);
727 bzero(&bytes
, sizeof(bytes
));
732 CC_SHA1_Final(bytes
.sha1_bytes
, &ctx
);
734 // create UUID string
735 uuid
= CFUUIDCreateFromUUIDBytes(NULL
, bytes
.uuid_bytes
);
736 uuid_str
= CFUUIDCreateString(NULL
, uuid
);
744 updatePreConfiguredConfiguration(SCPreferencesRef prefs
)
749 Boolean updated
= FALSE
;
751 range
.length
= (preconfigured_names
!= NULL
) ? CFArrayGetCount(preconfigured_names
) : 0;
752 if (range
.length
== 0) {
753 // if no [preconfigured] interfaces
758 set
= SCNetworkSetCopyCurrent(prefs
);
763 * Check for (and remove) any network services associated with
764 * a pre-configured interface from the prefs.
766 services
= SCNetworkServiceCopyAll(prefs
);
767 if (services
!= NULL
) {
770 n
= CFArrayGetCount(services
);
771 for (CFIndex i
= 0; i
< n
; i
++) {
773 SCNetworkInterfaceRef interface
;
774 SCNetworkServiceRef service
;
776 service
= CFArrayGetValueAtIndex(services
, i
);
778 interface
= SCNetworkServiceGetInterface(service
);
779 if (interface
== NULL
) {
784 bsdName
= SCNetworkInterfaceGetBSDName(interface
);
785 if (bsdName
== NULL
) {
786 // if no interface name
790 if (!CFArrayContainsValue(preconfigured_names
, range
, bsdName
)) {
791 // if not preconfigured
795 // remove [preconfigured] network service from the prefs
796 SC_log(LOG_NOTICE
, "removing network service for %@", bsdName
);
797 ok
= SCNetworkServiceRemove(service
);
799 SC_log(LOG_ERR
, "SCNetworkServiceRemove() failed: %s",
800 SCErrorString(SCError()));
809 // commit the updated prefs ... but don't apply
810 ok
= SCPreferencesCommitChanges(prefs
);
812 if (SCError() != EROFS
) {
813 SC_log(LOG_ERR
, "SCPreferencesCommitChanges() failed: %s",
814 SCErrorString(SCError()));
820 * Now, add a new network service for each pre-configured interface
822 range
.length
= (preconfigured_interfaces
!= NULL
) ? CFArrayGetCount(preconfigured_interfaces
) : 0;
823 for (CFIndex i
= 0; i
< range
.length
; i
++) {
825 SCNetworkInterfaceRef interface
= CFArrayGetValueAtIndex(preconfigured_interfaces
, i
);
826 SCNetworkServiceRef service
;
828 bsdName
= SCNetworkInterfaceGetBSDName(interface
);
829 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
831 SC_log(LOG_ERR
, "could not establish network service for %@: %s",
833 SCErrorString(SCError()));
837 service
= copyInterfaceService(set
, bsdName
);
838 if (service
!= NULL
) {
839 CFStringRef serviceID
;
841 serviceID
= copyInterfaceUUID(bsdName
);
842 if (serviceID
!= NULL
) {
843 ok
= _SCNetworkServiceSetServiceID(service
, serviceID
);
844 CFRelease(serviceID
);
846 SC_log(LOG_ERR
, "_SCNetworkServiceSetServiceID() failed: %s",
847 SCErrorString(SCError()));
848 // ... and keep whatever random UUID was created for the service
851 SC_log(LOG_ERR
, "could not create serviceID for %@", bsdName
);
852 // ... and we'll use whatever random UUID was created for the service
855 SC_log(LOG_INFO
, "network service %@ added for %@",
856 SCNetworkServiceGetServiceID(service
),
861 SC_log(LOG_ERR
, "could not find network service for %@", bsdName
);
873 updateSCDynamicStore(SCPreferencesRef prefs
)
875 CFStringRef current
= NULL
;
876 CFDateRef date
= NULL
;
877 CFMutableDictionaryRef dict
= NULL
;
878 CFDictionaryRef global
= NULL
;
883 CFMutableArrayRef patterns
;
884 CFDictionaryRef set
= NULL
;
887 * initialize old preferences, new preferences, an array
888 * of keys which have not changed, and an array of keys
889 * to be removed (cleaned up).
892 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
893 pattern
= CFStringCreateWithFormat(NULL
,
896 kSCDynamicStoreDomainSetup
);
897 CFArrayAppendValue(patterns
, pattern
);
898 dict
= (CFMutableDictionaryRef
)SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
902 currentPrefs
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
905 currentPrefs
= CFDictionaryCreateMutable(NULL
,
907 &kCFTypeDictionaryKeyCallBacks
,
908 &kCFTypeDictionaryValueCallBacks
);
911 unchangedPrefsKeys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
913 i
= CFDictionaryGetCount(currentPrefs
);
915 const void **currentKeys
;
918 currentKeys
= CFAllocatorAllocate(NULL
, i
* sizeof(CFStringRef
), 0);
919 CFDictionaryGetKeysAndValues(currentPrefs
, currentKeys
, NULL
);
920 array
= CFArrayCreate(NULL
, currentKeys
, i
, &kCFTypeArrayCallBacks
);
921 removedPrefsKeys
= CFArrayCreateMutableCopy(NULL
, 0, array
);
923 CFAllocatorDeallocate(NULL
, currentKeys
);
925 removedPrefsKeys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
929 * The "newPrefs" dictionary will contain the new / updated
930 * configuration which will be written to the configuration cache.
932 newPrefs
= CFDictionaryCreateMutable(NULL
,
934 &kCFTypeDictionaryKeyCallBacks
,
935 &kCFTypeDictionaryValueCallBacks
);
938 * create status dictionary associated with current configuration
939 * information including:
940 * - current set "name" to cache
941 * - time stamp indicating when the cache preferences were
944 dict
= CFDictionaryCreateMutable(NULL
,
946 &kCFTypeDictionaryKeyCallBacks
,
947 &kCFTypeDictionaryValueCallBacks
);
948 date
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
953 keys
= SCPreferencesCopyKeyList(prefs
);
954 if ((keys
== NULL
) || (CFArrayGetCount(keys
) == 0)) {
955 SC_log(LOG_NOTICE
, "updateConfiguration(): no preferences");
960 * get "global" system preferences
962 global
= SCPreferencesGetValue(prefs
, kSCPrefSystem
);
964 /* if no global preferences are defined */
968 if (!isA_CFDictionary(global
)) {
969 SC_log(LOG_NOTICE
, "updateConfiguration(): %@ is not a dictionary",
974 /* flatten property list */
975 flatten(prefs
, CFSTR("/"), global
);
980 * get current set name
982 current
= SCPreferencesGetValue(prefs
, kSCPrefCurrentSet
);
984 /* if current set not defined */
988 if (!isA_CFString(current
)) {
989 SC_log(LOG_NOTICE
, "updateConfiguration(): %@ is not a string",
997 set
= SCPreferencesPathGetValue(prefs
, current
);
999 /* if error with path */
1000 SC_log(LOG_NOTICE
, "%@ value (%@) not valid",
1006 if (!isA_CFDictionary(set
)) {
1007 SC_log(LOG_NOTICE
, "updateConfiguration(): %@ is not a dictionary",
1012 /* flatten property list */
1013 flatten(prefs
, CFSTR("/"), set
);
1015 CFDictionarySetValue(dict
, kSCDynamicStorePropSetupCurrentSet
, current
);
1019 /* add last updated time stamp */
1020 CFDictionarySetValue(dict
, kSCDynamicStorePropSetupLastUpdated
, date
);
1022 /* add Setup: key */
1023 CFDictionarySetValue(newPrefs
, kSCDynamicStoreDomainSetup
, dict
);
1025 /* compare current and new preferences */
1026 CFDictionaryApplyFunction(newPrefs
, updateCache
, NULL
);
1028 /* remove those keys which have not changed from the update */
1029 n
= CFArrayGetCount(unchangedPrefsKeys
);
1030 for (i
= 0; i
< n
; i
++) {
1033 key
= CFArrayGetValueAtIndex(unchangedPrefsKeys
, i
);
1034 CFDictionaryRemoveValue(newPrefs
, key
);
1037 /* Update the dynamic store */
1039 if (!SCDynamicStoreSetMultiple(store
, newPrefs
, removedPrefsKeys
, NULL
)) {
1040 SC_log(LOG_NOTICE
, "SCDynamicStoreSetMultiple() failed: %s", SCErrorString(SCError()));
1043 SC_log(LOG_DEBUG
, "SCDynamicStore\nset: %@\nremove: %@",
1048 CFRelease(currentPrefs
);
1049 CFRelease(newPrefs
);
1050 CFRelease(unchangedPrefsKeys
);
1051 CFRelease(removedPrefsKeys
);
1052 if (dict
) CFRelease(dict
);
1053 if (date
) CFRelease(date
);
1054 if (keys
) CFRelease(keys
);
1060 updateConfiguration(SCPreferencesRef prefs
,
1061 SCPreferencesNotification notificationType
,
1064 #pragma unused(info)
1065 os_activity_t activity
;
1067 activity
= os_activity_create("processing [SC] preferences.plist changes",
1068 OS_ACTIVITY_CURRENT
,
1069 OS_ACTIVITY_FLAG_DEFAULT
);
1070 os_activity_scope(activity
);
1072 #if !TARGET_OS_IPHONE
1073 if ((notificationType
& kSCPreferencesNotificationCommit
) == kSCPreferencesNotificationCommit
) {
1074 SCNetworkSetRef current
;
1076 current
= SCNetworkSetCopyCurrent(prefs
);
1077 if (current
!= NULL
) {
1078 /* network configuration available, disable template creation */
1079 haveConfiguration
= TRUE
;
1083 #endif /* !TARGET_OS_IPHONE */
1085 if ((notificationType
& kSCPreferencesNotificationApply
) != kSCPreferencesNotificationApply
) {
1089 SC_log(LOG_INFO
, "updating configuration");
1091 /* add any [Apple] pre-configured network services */
1092 updatePreConfiguredConfiguration(prefs
);
1094 /* update SCDynamicStore (Setup:) */
1095 updateSCDynamicStore(prefs
);
1097 /* finished with current prefs, wait for changes */
1099 SCPreferencesSynchronize(prefs
);
1104 os_release(activity
);
1112 prime_PreferencesMonitor()
1114 SC_log(LOG_DEBUG
, "prime() called");
1116 /* load the initial configuration from the database */
1117 updateConfiguration(prefs
, kSCPreferencesNotificationApply
, (void *)store
);
1125 load_PreferencesMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
1127 #pragma unused(bundle)
1128 #pragma unused(bundleVerbose)
1129 SC_log(LOG_DEBUG
, "load() called");
1130 SC_log(LOG_DEBUG
, " bundle ID = %@", CFBundleGetIdentifier(bundle
));
1132 /* open a SCDynamicStore session to allow cache updates */
1133 store
= SCDynamicStoreCreate(NULL
,
1134 CFSTR("PreferencesMonitor.bundle"),
1137 if (store
== NULL
) {
1138 SC_log(LOG_NOTICE
, "SCDynamicStoreCreate() failed: %s", SCErrorString(SCError()));
1142 /* open a SCPreferences session */
1144 prefs
= SCPreferencesCreate(NULL
, CFSTR("PreferencesMonitor.bundle"), NULL
);
1146 prefs
= SCPreferencesCreate(NULL
, CFSTR("PreferencesMonitor.bundle"), CFSTR("/tmp/preferences.plist"));
1148 if (prefs
!= NULL
) {
1149 Boolean need_update
= FALSE
;
1150 CFStringRef new_model
;
1152 new_model
= _SC_hw_model(FALSE
);
1154 /* Need to regenerate the new configuration for new model */
1155 if (new_model
!= NULL
) {
1156 CFStringRef old_model
;
1158 old_model
= SCPreferencesGetValue(prefs
, MODEL
);
1159 if (old_model
!= NULL
&& !_SC_CFEqual(old_model
, new_model
)) {
1162 restorePrefs
= previousConfigurationAvailable();
1167 SCNetworkSetRef current
;
1169 current
= SCNetworkSetCopyCurrent(prefs
);
1170 if (current
!= NULL
) {
1171 /* network configuration available, disable template creation */
1172 haveConfiguration
= TRUE
;
1177 SC_log(LOG_NOTICE
, "SCPreferencesCreate() failed: %s", SCErrorString(SCError()));
1182 * register for change notifications.
1184 if (!SCPreferencesSetCallback(prefs
, updateConfiguration
, NULL
)) {
1185 SC_log(LOG_NOTICE
, "SCPreferencesSetCallBack() failed: %s", SCErrorString(SCError()));
1189 if (!SCPreferencesScheduleWithRunLoop(prefs
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
)) {
1190 SC_log(LOG_NOTICE
, "SCPreferencesScheduleWithRunLoop() failed: %s", SCErrorString(SCError()));
1195 * watch InterfaceNamer and KernelEventMonitor changes to know when
1196 * the IORegistry has quiesced (to create the initial configuration
1197 * template), to track any pre-configured interfaces, and to ensure
1198 * that we create a network service for any active interfaces.
1200 watchSCDynamicStore();
1201 storeCallback(store
, NULL
, NULL
);
1207 if (store
!= NULL
) CFRelease(store
);
1208 if (prefs
!= NULL
) CFRelease(prefs
);
1209 haveConfiguration
= TRUE
;
1217 main(int argc
, char **argv
)
1220 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
1222 load_PreferencesMonitor(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);
1223 prime_PreferencesMonitor();