2  * Copyright (c) 2000-2005 Apple Computer, 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> 
  39 #include <sys/types.h> 
  44 #include <SystemConfiguration/SystemConfiguration.h> 
  45 #include <SystemConfiguration/SCPrivate.h> 
  46 #include <SystemConfiguration/SCValidation.h> 
  49 static SCPreferencesRef         prefs           
= NULL
; 
  50 static SCDynamicStoreRef        store           
= NULL
; 
  52 static CFMutableDictionaryRef   currentPrefs
;           /* current prefs */ 
  53 static CFMutableDictionaryRef   newPrefs
;               /* new prefs */ 
  54 static CFMutableArrayRef        unchangedPrefsKeys
;     /* new prefs keys which match current */ 
  55 static CFMutableArrayRef        removedPrefsKeys
;       /* old prefs keys to be removed */ 
  57 static Boolean                  _verbose        
= FALSE
; 
  61 updateCache(const void *key
, const void *value
, void *context
) 
  63         CFStringRef             configKey       
= (CFStringRef
)key
; 
  64         CFPropertyListRef       configData      
= (CFPropertyListRef
)value
; 
  65         CFPropertyListRef       cacheData
; 
  68         cacheData 
= CFDictionaryGetValue(currentPrefs
, configKey
); 
  71                 if (CFEqual(cacheData
, configData
)) { 
  73                          * if the old & new property list values have 
  74                          * not changed then we don't need to update 
  77                         CFArrayAppendValue(unchangedPrefsKeys
, configKey
); 
  81         /* in any case, this key should not be removed */ 
  82         i 
= CFArrayGetFirstIndexOfValue(removedPrefsKeys
, 
  83                                         CFRangeMake(0, CFArrayGetCount(removedPrefsKeys
)), 
  85         if (i 
!= kCFNotFound
) { 
  86                 CFArrayRemoveValueAtIndex(removedPrefsKeys
, i
); 
  94 flatten(SCPreferencesRef        prefs
, 
  98         CFDictionaryRef         subset
; 
 100         CFMutableDictionaryRef  myDict
; 
 107         if (!CFDictionaryGetValueIfPresent(base
, kSCResvLink
, (const void **)&link
)) { 
 108                 /* if this dictionary is not linked */ 
 111                 /* if __LINK__ key is present */ 
 112                 subset 
= SCPreferencesPathGetValue(prefs
, link
); 
 114                         /* if error with link */ 
 116                               CFSTR("SCPreferencesPathGetValue(,%@,) failed: %s"), 
 118                               SCErrorString(SCError())); 
 123         if (CFDictionaryContainsKey(subset
, kSCResvInactive
)) { 
 124                 /* if __INACTIVE__ key is present */ 
 128         myKey 
= CFStringCreateWithFormat(NULL
, 
 131                                          kSCDynamicStoreDomainSetup
, 
 134         myDict 
= (CFMutableDictionaryRef
)CFDictionaryGetValue(newPrefs
, myKey
); 
 136                 myDict 
= CFDictionaryCreateMutableCopy(NULL
, 
 138                                                        (CFDictionaryRef
)myDict
); 
 140                 myDict 
= CFDictionaryCreateMutable(NULL
, 
 142                                                    &kCFTypeDictionaryKeyCallBacks
, 
 143                                                    &kCFTypeDictionaryValueCallBacks
); 
 146         nKeys 
= CFDictionaryGetCount(subset
); 
 148                 keys  
= CFAllocatorAllocate(NULL
, nKeys 
* sizeof(CFStringRef
)      , 0); 
 149                 vals  
= CFAllocatorAllocate(NULL
, nKeys 
* sizeof(CFPropertyListRef
), 0); 
 150                 CFDictionaryGetKeysAndValues(subset
, keys
, vals
); 
 151                 for (i 
= 0; i 
< nKeys
; i
++) { 
 152                         if (CFGetTypeID((CFTypeRef
)vals
[i
]) != CFDictionaryGetTypeID()) { 
 153                                 /* add this key/value to the current dictionary */ 
 154                                 CFDictionarySetValue(myDict
, keys
[i
], vals
[i
]); 
 158                                 /* flatten [sub]dictionaries */ 
 159                                 subKey 
= CFStringCreateWithFormat(NULL
, 
 163                                                                   CFEqual(key
, CFSTR("/")) ? "" : "/", 
 165                                 flatten(prefs
, subKey
, vals
[i
]); 
 169                 CFAllocatorDeallocate(NULL
, keys
); 
 170                 CFAllocatorDeallocate(NULL
, vals
); 
 173         if (CFDictionaryGetCount(myDict
) > 0) { 
 174                 /* add this dictionary to the new preferences */ 
 175                 CFDictionarySetValue(newPrefs
, myKey
, myDict
); 
 186 updateConfiguration(SCPreferencesRef            prefs
, 
 187                     SCPreferencesNotification   notificationType
, 
 190         CFStringRef             current         
= NULL
; 
 191         CFDateRef               date            
= NULL
; 
 192         CFMutableDictionaryRef  dict            
= NULL
; 
 193         CFDictionaryRef         global          
= NULL
; 
 198         CFMutableArrayRef       patterns
; 
 199         CFDictionaryRef         set             
= NULL
; 
 201         if ((notificationType 
& kSCPreferencesNotificationApply
) != kSCPreferencesNotificationApply
) { 
 205         SCLog(_verbose
, LOG_DEBUG
, CFSTR("updating configuration")); 
 208          * initialize old preferences, new preferences, an array 
 209          * of keys which have not changed, and an array of keys 
 210          * to be removed (cleaned up). 
 213         patterns 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 214         pattern  
= CFStringCreateWithFormat(NULL
, 
 217                                             kSCDynamicStoreDomainSetup
); 
 218         CFArrayAppendValue(patterns
, pattern
); 
 219         dict 
= (CFMutableDictionaryRef
)SCDynamicStoreCopyMultiple(store
, NULL
, patterns
); 
 223                 currentPrefs 
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
); 
 226                 currentPrefs 
= CFDictionaryCreateMutable(NULL
, 
 228                                                          &kCFTypeDictionaryKeyCallBacks
, 
 229                                                          &kCFTypeDictionaryValueCallBacks
); 
 232         unchangedPrefsKeys 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 234         i 
= CFDictionaryGetCount(currentPrefs
); 
 236                 const void      **currentKeys
; 
 239                 currentKeys 
= CFAllocatorAllocate(NULL
, i 
* sizeof(CFStringRef
), 0); 
 240                 CFDictionaryGetKeysAndValues(currentPrefs
, currentKeys
, NULL
); 
 241                 array 
= CFArrayCreate(NULL
, currentKeys
, i
, &kCFTypeArrayCallBacks
); 
 242                 removedPrefsKeys 
= CFArrayCreateMutableCopy(NULL
, 0, array
); 
 244                 CFAllocatorDeallocate(NULL
, currentKeys
); 
 246                 removedPrefsKeys 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 250          * The "newPrefs" dictionary will contain the new / updated 
 251          * configuration which will be written to the configuration cache. 
 253         newPrefs 
= CFDictionaryCreateMutable(NULL
, 
 255                                                  &kCFTypeDictionaryKeyCallBacks
, 
 256                                                  &kCFTypeDictionaryValueCallBacks
); 
 259          * create status dictionary associated with current configuration 
 260          * information including: 
 261          *   - current set "name" to cache 
 262          *   - time stamp indicating when the cache preferences were 
 265         dict 
= CFDictionaryCreateMutable(NULL
, 
 267                                          &kCFTypeDictionaryKeyCallBacks
, 
 268                                          &kCFTypeDictionaryValueCallBacks
); 
 269         date 
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent()); 
 274         keys 
= SCPreferencesCopyKeyList(prefs
); 
 275         if ((keys 
== NULL
) || (CFArrayGetCount(keys
) == 0)) { 
 276                 SCLog(TRUE
, LOG_NOTICE
, CFSTR("updateConfiguration(): no preferences.")); 
 281          * get "global" system preferences 
 283         global 
= SCPreferencesGetValue(prefs
, kSCPrefSystem
); 
 285                 /* if no global preferences are defined */ 
 289         if (!isA_CFDictionary(global
)) { 
 291                       CFSTR("updateConfiguration(): %@ is not a dictionary."), 
 296         /* flatten property list */ 
 297         flatten(prefs
, CFSTR("/"), global
); 
 302          * get current set name 
 304         current 
= SCPreferencesGetValue(prefs
, kSCPrefCurrentSet
); 
 306                 /* if current set not defined */ 
 310         if (!isA_CFString(current
)) { 
 312                       CFSTR("updateConfiguration(): %@ is not a string."), 
 320         set 
= SCPreferencesPathGetValue(prefs
, current
); 
 322                 /* if error with path */ 
 324                       CFSTR("%@ value (%@) not valid"), 
 330         if (!isA_CFDictionary(set
)) { 
 332                       CFSTR("updateConfiguration(): %@ is not a dictionary."), 
 337         /* flatten property list */ 
 338         flatten(prefs
, CFSTR("/"), set
); 
 340         CFDictionarySetValue(dict
, kSCDynamicStorePropSetupCurrentSet
, current
); 
 344         /* add last updated time stamp */ 
 345         CFDictionarySetValue(dict
, kSCDynamicStorePropSetupLastUpdated
, date
); 
 348         CFDictionarySetValue(newPrefs
, kSCDynamicStoreDomainSetup
, dict
); 
 350         /* compare current and new preferences */ 
 351         CFDictionaryApplyFunction(newPrefs
, updateCache
, NULL
); 
 353         /* remove those keys which have not changed from the update */ 
 354         n 
= CFArrayGetCount(unchangedPrefsKeys
); 
 355         for (i 
= 0; i 
< n
; i
++) { 
 358                 key 
= CFArrayGetValueAtIndex(unchangedPrefsKeys
, i
); 
 359                 CFDictionaryRemoveValue(newPrefs
, key
); 
 362         /* Update the dynamic store */ 
 363         if (!SCDynamicStoreSetMultiple(store
, newPrefs
, removedPrefsKeys
, NULL
)) { 
 365                       CFSTR("SCDynamicStoreSetMultiple() failed: %s"), 
 366                       SCErrorString(SCError())); 
 369         /* finished with current prefs, wait for changes */ 
 370         SCPreferencesSynchronize(prefs
); 
 372         CFRelease(currentPrefs
); 
 374         CFRelease(unchangedPrefsKeys
); 
 375         CFRelease(removedPrefsKeys
); 
 376         if (dict
)       CFRelease(dict
); 
 377         if (date
)       CFRelease(date
); 
 378         if (keys
)       CFRelease(keys
); 
 385 stop_PreferencesMonitor(CFRunLoopSourceRef stopRls
) 
 390                 if (!SCPreferencesUnscheduleFromRunLoop(prefs
, 
 391                                                         CFRunLoopGetCurrent(), 
 392                                                         kCFRunLoopDefaultMode
)) { 
 394                               CFSTR("SCPreferencesUnscheduleFromRunLoop() failed: %s"), 
 395                               SCErrorString(SCError())); 
 406         CFRunLoopSourceSignal(stopRls
); 
 413 prime_PreferencesMonitor() 
 415         SCLog(_verbose
, LOG_DEBUG
, CFSTR("prime() called")); 
 417         /* load the initial configuration from the database */ 
 418         updateConfiguration(prefs
, kSCPreferencesNotificationApply
, (void *)store
); 
 426 load_PreferencesMonitor(CFBundleRef bundle
, Boolean bundleVerbose
) 
 432         SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called")); 
 433         SCLog(_verbose
, LOG_DEBUG
, CFSTR("  bundle ID = %@"), CFBundleGetIdentifier(bundle
)); 
 435         /* open a SCDynamicStore session to allow cache updates */ 
 436         store 
= SCDynamicStoreCreate(NULL
, CFSTR("PreferencesMonitor.bundle"), NULL
, NULL
); 
 439                       CFSTR("SCDynamicStoreCreate() failed: %s"), 
 440                       SCErrorString(SCError())); 
 444         /* open a SCPreferences session */ 
 445         prefs 
= SCPreferencesCreate(NULL
, CFSTR("PreferencesMonitor.bundle"), NULL
); 
 448                       CFSTR("SCPreferencesCreate() failed: %s"), 
 449                       SCErrorString(SCError())); 
 453         if (!SCPreferencesSetCallback(prefs
, updateConfiguration
, NULL
)) { 
 455                       CFSTR("SCPreferencesSetCallBack() failed: %s"), 
 456                       SCErrorString(SCError())); 
 461          * register for change notifications. 
 463         if (!SCPreferencesScheduleWithRunLoop(prefs
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
)) { 
 465                       CFSTR("SCPreferencesScheduleWithRunLoop() failed: %s"), 
 466                       SCErrorString(SCError())); 
 474         if (store
)      CFRelease(store
); 
 475         if (prefs
)      CFRelease(prefs
); 
 483 main(int argc
, char **argv
) 
 486         _sc_verbose 
= (argc 
> 1) ? TRUE 
: FALSE
; 
 488         load_PreferencesMonitor(CFBundleGetMainBundle(), (argc 
> 1) ? TRUE 
: FALSE
); 
 489         prime_PreferencesMonitor();