2  * Copyright(c) 2000-2019 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  * February 16, 2004            Allan Nathanson <ajn@apple.com> 
  28  * - add preference notification APIs 
  30  * June 1, 2001                 Allan Nathanson <ajn@apple.com> 
  31  * - public API conversion 
  33  * November 9, 2000             Allan Nathanson <ajn@apple.com> 
  37 #include <TargetConditionals.h> 
  42 #include <sys/errno.h> 
  43 #include <sys/cdefs.h> 
  44 #include <dispatch/dispatch.h> 
  46 #include "SCPreferencesInternal.h" 
  48 #include "SCHelper_client.h" 
  49 #include "dy_framework.h" 
  52 const AuthorizationRef  kSCPreferencesUseEntitlementAuthorization       
= (AuthorizationRef
)CFSTR("UseEntitlement"); 
  55 __private_extern__ os_log_t
 
  56 __log_SCPreferences(void) 
  58         static os_log_t log     
= NULL
; 
  61                 log 
= os_log_create("com.apple.SystemConfiguration", "SCPreferences"); 
  68 static __inline__ CFTypeRef
 
  69 isA_SCPreferences(CFTypeRef obj
) 
  71         return (isA_CFType(obj
, SCPreferencesGetTypeID())); 
  76 __SCPreferencesCopyDescription(CFTypeRef cf
) { 
  77         CFAllocatorRef          allocator       
= CFGetAllocator(cf
); 
  78         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)cf
; 
  79         CFMutableStringRef      result
; 
  81         result 
= CFStringCreateMutable(allocator
, 0); 
  82         CFStringAppendFormat(result
, NULL
, CFSTR("<SCPreferences %p [%p]> {"), cf
, allocator
); 
  83         CFStringAppendFormat(result
, NULL
, CFSTR("name = %@"), prefsPrivate
->name
); 
  84         CFStringAppendFormat(result
, NULL
, CFSTR(", id = %@"), 
  85                              prefsPrivate
->prefsID 
!= NULL 
? prefsPrivate
->prefsID 
: CFSTR("[default]")); 
  86         CFStringAppendFormat(result
, NULL
, CFSTR(", path = %s"), 
  87                              prefsPrivate
->newPath 
!= NULL 
? prefsPrivate
->newPath 
: prefsPrivate
->path
); 
  88         if (prefsPrivate
->accessed
) { 
  89                 CFStringAppendFormat(result
, NULL
, CFSTR(", accessed")); 
  91         if (prefsPrivate
->changed
) { 
  92                 CFStringAppendFormat(result
, NULL
, CFSTR(", changed")); 
  94         if (prefsPrivate
->locked
) { 
  95                 CFStringAppendFormat(result
, NULL
, CFSTR(", locked")); 
  97         if (prefsPrivate
->helper_port 
!= MACH_PORT_NULL
) { 
  98                 CFStringAppendFormat(result
, NULL
, CFSTR(", helper port = 0x%x"), prefsPrivate
->helper_port
); 
 100         CFStringAppendFormat(result
, NULL
, CFSTR("}")); 
 107 __SCPreferencesDeallocate(CFTypeRef cf
) 
 109         SCPreferencesRef        prefs           
= (SCPreferencesRef
)cf
; 
 110         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 112         SC_log(LOG_DEBUG
, "release %@", prefsPrivate
); 
 114         if (prefsPrivate
->locked
) { 
 115                 __SCPreferencesUpdateLockedState(prefs
, FALSE
); 
 118         /* release resources */ 
 120         pthread_mutex_destroy(&prefsPrivate
->lock
); 
 122         if (prefsPrivate
->name
)                 CFRelease(prefsPrivate
->name
); 
 123         if (prefsPrivate
->prefsID
)              CFRelease(prefsPrivate
->prefsID
); 
 124         if (prefsPrivate
->options
)              CFRelease(prefsPrivate
->options
); 
 125         if (prefsPrivate
->path
)                 CFAllocatorDeallocate(NULL
, prefsPrivate
->path
); 
 126         if (prefsPrivate
->newPath
)              CFAllocatorDeallocate(NULL
, prefsPrivate
->newPath
); 
 127         if (prefsPrivate
->lockFD 
!= -1) { 
 128                 if (prefsPrivate
->lockPath 
!= NULL
) { 
 129                         unlink(prefsPrivate
->lockPath
); 
 131                 close(prefsPrivate
->lockFD
); 
 133         if (prefsPrivate
->lockPath
)             CFAllocatorDeallocate(NULL
, prefsPrivate
->lockPath
); 
 134         if (prefsPrivate
->signature
)            CFRelease(prefsPrivate
->signature
); 
 135         if (prefsPrivate
->sessionNoO_EXLOCK 
!= NULL
) { 
 136                 CFRelease(prefsPrivate
->sessionNoO_EXLOCK
); 
 138         if (prefsPrivate
->sessionKeyLock
)       CFRelease(prefsPrivate
->sessionKeyLock
); 
 139         if (prefsPrivate
->sessionKeyCommit
)     CFRelease(prefsPrivate
->sessionKeyCommit
); 
 140         if (prefsPrivate
->sessionKeyApply
)      CFRelease(prefsPrivate
->sessionKeyApply
); 
 141         if (prefsPrivate
->rlsContext
.release 
!= NULL
) { 
 142                 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
); 
 144         if (prefsPrivate
->prefs
)                CFRelease(prefsPrivate
->prefs
); 
 145         if (prefsPrivate
->authorizationData 
!= NULL
) CFRelease(prefsPrivate
->authorizationData
); 
 146         if (prefsPrivate
->helper_port 
!= MACH_PORT_NULL
) { 
 147                 (void) _SCHelperExec(prefsPrivate
->helper_port
, 
 148                                      SCHELPER_MSG_PREFS_CLOSE
, 
 152                 _SCHelperClose(&prefsPrivate
->helper_port
); 
 159 static CFTypeID __kSCPreferencesTypeID  
= _kCFRuntimeNotATypeID
; 
 162 static const CFRuntimeClass __SCPreferencesClass 
= { 
 164         "SCPreferences",                // className 
 167         __SCPreferencesDeallocate
,      // dealloc 
 170         NULL
,                           // copyFormattingDesc 
 171         __SCPreferencesCopyDescription  
// copyDebugDesc 
 175 static pthread_once_t initialized       
= PTHREAD_ONCE_INIT
; 
 178 __SCPreferencesInitialize(void) { 
 179         /* register with CoreFoundation */ 
 180         __kSCPreferencesTypeID 
= _CFRuntimeRegisterClass(&__SCPreferencesClass
); 
 185 static SCPreferencesPrivateRef
 
 186 __SCPreferencesCreatePrivate(CFAllocatorRef     allocator
) 
 188         SCPreferencesPrivateRef prefsPrivate
; 
 191         /* initialize runtime */ 
 192         pthread_once(&initialized
, __SCPreferencesInitialize
); 
 194         /* allocate prefs session */ 
 195         size  
= sizeof(SCPreferencesPrivate
) - sizeof(CFRuntimeBase
); 
 196         prefsPrivate 
= (SCPreferencesPrivateRef
)_CFRuntimeCreateInstance(allocator
, 
 197                                                                          __kSCPreferencesTypeID
, 
 200         if (prefsPrivate 
== NULL
) { 
 204         /* initialize non-zero/NULL members */ 
 205         pthread_mutex_init(&prefsPrivate
->lock
, NULL
); 
 206         prefsPrivate
->lockFD                            
= -1; 
 207         prefsPrivate
->isRoot                            
= (geteuid() == 0); 
 213 __private_extern__ Boolean
 
 214 __SCPreferencesCreate_helper(SCPreferencesRef prefs
) 
 216         CFDataRef               data            
= NULL
; 
 217         CFMutableDictionaryRef  info
; 
 220         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 221         uint32_t                status          
= kSCStatusOK
; 
 223         uint32_t                pid             
= getpid(); 
 226         ok 
= _SCHelperOpen(prefsPrivate
->authorizationData
, 
 227                            &prefsPrivate
->helper_port
); 
 232         // create a dictionary of information to pass to the helper 
 233         info 
= CFDictionaryCreateMutable(NULL
, 
 235                                          &kCFTypeDictionaryKeyCallBacks
, 
 236                                          &kCFTypeDictionaryValueCallBacks
); 
 239         if (prefsPrivate
->prefsID 
!= NULL
) { 
 240                 CFDictionarySetValue(info
, CFSTR("prefsID"), prefsPrivate
->prefsID
); 
 244         if (prefsPrivate
->options 
!= NULL
) { 
 245                 CFDictionarySetValue(info
, CFSTR("options"), prefsPrivate
->options
); 
 248         // save preferences session "name" 
 249         CFDictionarySetValue(info
, CFSTR("name"), prefsPrivate
->name
); 
 252         num 
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &pid
); 
 253         CFDictionarySetValue(info
, CFSTR("PID"), num
); 
 257         str 
= CFStringCreateWithCString(NULL
, getprogname(), kCFStringEncodingUTF8
); 
 258         CFDictionarySetValue(info
, CFSTR("PROC_NAME"), str
); 
 261         // serialize the info 
 262         ok 
= _SCSerialize(info
, &data
, NULL
, NULL
); 
 264         if (data 
== NULL 
|| !ok
) { 
 268         // have the helper "open" the prefs 
 269         ok 
= _SCHelperExec(prefsPrivate
->helper_port
, 
 270                            SCHELPER_MSG_PREFS_OPEN
, 
 274         if (data 
!= NULL
) CFRelease(data
); 
 279         if (status 
!= kSCStatusOK
) { 
 288         if (prefsPrivate
->helper_port 
!= MACH_PORT_NULL
) { 
 289                 _SCHelperClose(&prefsPrivate
->helper_port
); 
 292         status 
= kSCStatusAccessError
; 
 303 __SCPreferencesAccess_helper(SCPreferencesRef prefs
) 
 306         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 307         CFDictionaryRef         serverDict      
= NULL
; 
 308         CFDictionaryRef         serverPrefs     
= NULL
; 
 309         CFDictionaryRef         serverSignature 
= NULL
; 
 310         uint32_t                status          
= kSCStatusOK
; 
 311         CFDataRef               reply           
= NULL
; 
 313         if (prefsPrivate
->helper_port 
== MACH_PORT_NULL
) { 
 314                 ok 
= __SCPreferencesCreate_helper(prefs
); 
 320         // have the helper "access" the prefs 
 321         ok 
= _SCHelperExec(prefsPrivate
->helper_port
, 
 322                            SCHELPER_MSG_PREFS_ACCESS
, 
 330         if (status 
!= kSCStatusOK
) { 
 338         ok 
= _SCUnserialize((CFPropertyListRef 
*)&serverDict
, reply
, NULL
, 0); 
 344         if (isA_CFDictionary(serverDict
)) { 
 345                 serverPrefs 
= CFDictionaryGetValue(serverDict
, CFSTR("preferences")); 
 346                 serverPrefs 
= isA_CFDictionary(serverPrefs
); 
 348                 serverSignature 
= CFDictionaryGetValue(serverDict
, CFSTR("signature")); 
 349                 serverSignature 
= isA_CFData(serverSignature
); 
 352         if ((serverPrefs 
== NULL
) || (serverSignature 
== NULL
)) { 
 353                 if (serverDict 
!= NULL
) CFRelease(serverDict
); 
 357         prefsPrivate
->prefs     
= CFDictionaryCreateMutableCopy(NULL
, 0, serverPrefs
); 
 358         prefsPrivate
->signature 
= CFRetain(serverSignature
); 
 359         prefsPrivate
->accessed  
= TRUE
; 
 360         CFRelease(serverDict
); 
 367         if (prefsPrivate
->helper_port 
!= MACH_PORT_NULL
) { 
 368                 _SCHelperClose(&prefsPrivate
->helper_port
); 
 371         status 
= kSCStatusAccessError
; 
 381 static SCPreferencesPrivateRef
 
 382 __SCPreferencesCreate(CFAllocatorRef    allocator
, 
 385                       CFDataRef         authorizationData
, 
 386                       CFDictionaryRef   options
) 
 388         SCPreferencesPrivateRef         prefsPrivate
; 
 389         int                             sc_status       
= kSCStatusOK
; 
 392          * allocate and initialize a new prefs session 
 394         prefsPrivate 
= __SCPreferencesCreatePrivate(allocator
); 
 395         if (prefsPrivate 
== NULL
) { 
 399         prefsPrivate
->name 
= CFStringCreateCopy(allocator
, name
); 
 400         if (prefsID 
!= NULL
) { 
 401                 prefsPrivate
->prefsID 
= CFStringCreateCopy(allocator
, prefsID
); 
 403         if (authorizationData 
!= NULL
) { 
 404                 prefsPrivate
->authorizationData 
= CFRetain(authorizationData
); 
 406         if (options 
!= NULL
) { 
 407                 prefsPrivate
->options 
= CFDictionaryCreateCopy(allocator
, options
); 
 413          * convert prefsID to path 
 415         prefsPrivate
->path 
= __SCPreferencesPath(allocator
, 
 417                                                  (prefsPrivate
->newPath 
== NULL
)); 
 418         if (prefsPrivate
->path 
== NULL
) { 
 419                 sc_status 
= kSCStatusFailed
; 
 423         if (access(prefsPrivate
->path
, R_OK
) == 0) { 
 430                         if ((prefsID 
== NULL
) || !CFStringHasPrefix(prefsID
, CFSTR("/"))) { 
 431                                 /* if default preference ID or relative path */ 
 432                                 if (prefsPrivate
->newPath 
== NULL
) { 
 434                                          * we've looked in the "new" prefs directory 
 435                                          * without success.  Save the "new" path and 
 436                                          * look in the "old" prefs directory. 
 438                                         prefsPrivate
->newPath 
= prefsPrivate
->path
; 
 442                                          * we've looked in both the "new" and "old" 
 443                                          * prefs directories without success.  Use 
 446                                         CFAllocatorDeallocate(NULL
, prefsPrivate
->path
); 
 447                                         prefsPrivate
->path 
= prefsPrivate
->newPath
; 
 448                                         prefsPrivate
->newPath 
= NULL
; 
 452                         /* no preference data, start fresh */ 
 453                         sc_status 
= kSCStatusNoConfigFile
; 
 457                         if (prefsPrivate
->authorizationData 
!= NULL
) { 
 458                                 /* no problem, we'll be using the helper */ 
 462                         SC_log(LOG_NOTICE
, "open() failed: %s", strerror(errno
)); 
 463                         sc_status 
= kSCStatusAccessError
; 
 466                         SC_log(LOG_NOTICE
, "open() failed: %s", strerror(errno
)); 
 467                         sc_status 
= kSCStatusFailed
; 
 473         CFRelease(prefsPrivate
); 
 474         _SCErrorSet(sc_status
); 
 480         _SCErrorSet(sc_status
); 
 485 __private_extern__ 
void 
 486 __SCPreferencesAccess(SCPreferencesRef  prefs
) 
 488         CFAllocatorRef          allocator       
= CFGetAllocator(prefs
); 
 490         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 493         if (prefsPrivate
->accessed
) { 
 494                 // if preference data has already been accessed 
 498         if (access(prefsPrivate
->path
, R_OK
) == 0) { 
 499                 fd 
= open(prefsPrivate
->path
, O_RDONLY
, 0644); 
 505                 if (fstat(fd
, &statBuf
) == -1) { 
 506                         SC_log(LOG_NOTICE
, "fstat() failed: %s", strerror(errno
)); 
 507                         memset(&statBuf
, 0, sizeof(statBuf
)); 
 512                                 /* no preference data, start fresh */ 
 516                                 if (prefsPrivate
->authorizationData 
!= NULL
) { 
 517                                         if (__SCPreferencesAccess_helper(prefs
)) { 
 520                                                 SC_log(LOG_NOTICE
, "__SCPreferencesAccess_helper() failed: %s", 
 521                                                        SCErrorString(SCError())); 
 527                                 SC_log(LOG_NOTICE
, "open() failed: %s", strerror(errno
)); 
 530                 memset(&statBuf
, 0, sizeof(statBuf
)); 
 533         if (prefsPrivate
->signature 
!= NULL
) CFRelease(prefsPrivate
->signature
); 
 534         prefsPrivate
->signature 
= __SCPSignatureFromStatbuf(&statBuf
); 
 536         if (statBuf
.st_size 
> 0) { 
 537                 CFDictionaryRef         dict
; 
 538                 CFErrorRef              error   
= NULL
; 
 539                 CFMutableDataRef        xmlData
; 
 542                  * extract property list 
 544                 xmlData 
= CFDataCreateMutable(allocator
, (CFIndex
)statBuf
.st_size
); 
 545                 CFDataSetLength(xmlData
, (CFIndex
)statBuf
.st_size
); 
 546                 if (read(fd
, (void *)CFDataGetBytePtr(xmlData
), (CFIndex
)statBuf
.st_size
) != (CFIndex
)statBuf
.st_size
) { 
 547                         /* corrupt prefs file, start fresh */ 
 548                         SC_log(LOG_INFO
, "read(): could not load preference data"); 
 557                 dict 
= CFPropertyListCreateWithData(allocator
, xmlData
, kCFPropertyListImmutable
, NULL
, &error
); 
 560                         /* corrupt prefs file, start fresh */ 
 562                                 SC_log(LOG_NOTICE
, "CFPropertyListCreateWithData(): %@", error
); 
 569                  * make sure that we've got a dictionary 
 571                 if (!isA_CFDictionary(dict
)) { 
 572                         /* corrupt prefs file, start fresh */ 
 573                         SC_log(LOG_INFO
, "CFGetTypeID(): not a dictionary"); 
 578                 prefsPrivate
->prefs 
= CFDictionaryCreateMutableCopy(allocator
, 0, dict
); 
 588         if (prefsPrivate
->prefs 
== NULL
) { 
 590                  * new file, create empty preferences 
 592 //              SC_log(LOG_INFO, "creating new preferences file"); 
 593                 prefsPrivate
->prefs 
= CFDictionaryCreateMutable(allocator
, 
 595                                                                 &kCFTypeDictionaryKeyCallBacks
, 
 596                                                                 &kCFTypeDictionaryValueCallBacks
); 
 597                 prefsPrivate
->changed 
= TRUE
; 
 600         SC_log(LOG_DEBUG
, "SCPreferences() access: %s, size=%lld", 
 601                prefsPrivate
->newPath 
? prefsPrivate
->newPath 
: prefsPrivate
->path
, 
 602                __SCPreferencesPrefsSize(prefs
)); 
 604         prefsPrivate
->accessed 
= TRUE
; 
 610 SCPreferencesCreate(CFAllocatorRef              allocator
, 
 614         SCPreferencesPrivateRef prefsPrivate
; 
 616         prefsPrivate 
= __SCPreferencesCreate(allocator
, name
, prefsID
, NULL
, NULL
); 
 617         if (prefsPrivate 
!= NULL
) { 
 618                 SC_log(LOG_DEBUG
, "create %@", prefsPrivate
); 
 621         return (SCPreferencesRef
)prefsPrivate
; 
 626 SCPreferencesCreateWithAuthorization(CFAllocatorRef     allocator
, 
 629                                      AuthorizationRef   authorization
) 
 631         SCPreferencesRef        prefs
; 
 633 #if     !TARGET_OS_IPHONE 
 634         if (authorization 
== NULL
) { 
 635                 authorization 
= kSCPreferencesUseEntitlementAuthorization
; 
 637 #else   // !TARGET_OS_IPHONE 
 638         authorization 
= kSCPreferencesUseEntitlementAuthorization
; 
 639 #endif  // !TARGET_OS_IPHONE 
 641         prefs 
= SCPreferencesCreateWithOptions(allocator
, name
, prefsID
, authorization
, NULL
); 
 647 SCPreferencesCreateWithOptions(CFAllocatorRef   allocator
, 
 650                                AuthorizationRef authorization
, 
 651                                CFDictionaryRef  options
) 
 653         CFDataRef                       authorizationData       
= NULL
; 
 654         SCPreferencesPrivateRef         prefsPrivate
; 
 656         if (options 
!= NULL
) { 
 657                 if (!isA_CFDictionary(options
)) { 
 658                         _SCErrorSet(kSCStatusInvalidArgument
); 
 663         if (authorization 
!= NULL
) { 
 664                 CFMutableDictionaryRef  authorizationDict
; 
 666                 CFStringRef             bundleID        
= NULL
; 
 668                 authorizationDict 
=  CFDictionaryCreateMutable(NULL
, 
 670                                                                &kCFTypeDictionaryKeyCallBacks
, 
 671                                                                &kCFTypeDictionaryValueCallBacks
); 
 672 #if     !TARGET_OS_IPHONE 
 673                 if (authorization 
!= kSCPreferencesUseEntitlementAuthorization
) { 
 675                         AuthorizationExternalForm       extForm
; 
 678                         os_status 
= AuthorizationMakeExternalForm(authorization
, &extForm
); 
 679                         if (os_status 
!= errAuthorizationSuccess
) { 
 680                                 SC_log(LOG_INFO
, "AuthorizationMakeExternalForm() failed"); 
 681                                 _SCErrorSet(kSCStatusInvalidArgument
); 
 682                                 CFRelease(authorizationDict
); 
 686                         data 
= CFDataCreate(NULL
, (const UInt8 
*)extForm
.bytes
, sizeof(extForm
.bytes
)); 
 687                         CFDictionaryAddValue(authorizationDict
, 
 688                                              kSCHelperAuthAuthorization
, 
 692 #endif  // !TARGET_OS_IPHONE 
 694                 /* get the application/executable/bundle name */ 
 695                 bundle 
= CFBundleGetMainBundle(); 
 696                 if (bundle 
!= NULL
) { 
 697                         bundleID 
= CFBundleGetIdentifier(bundle
); 
 698                         if (bundleID 
!= NULL
) { 
 703                                 url 
= CFBundleCopyExecutableURL(bundle
); 
 705                                         bundleID 
= CFURLCopyPath(url
); 
 710                         if (bundleID 
!= NULL
) { 
 711                                 if (CFEqual(bundleID
, CFSTR("/"))) { 
 717                 if (bundleID 
== NULL
) { 
 718                         bundleID 
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Unknown(%d)"), getpid()); 
 720                 CFDictionaryAddValue(authorizationDict
, 
 721                                      kSCHelperAuthCallerInfo
, 
 725                 if (authorizationDict 
!= NULL
) { 
 726                         (void) _SCSerialize((CFPropertyListRef
)authorizationDict
, 
 730                         CFRelease(authorizationDict
); 
 734         prefsPrivate 
= __SCPreferencesCreate(allocator
, name
, prefsID
, authorizationData
, options
); 
 735         if (prefsPrivate 
!= NULL
) { 
 736                 const char      *astr   
= ""; 
 737                 const char      *ostr   
= ""; 
 739                 if (options 
!= NULL
) { 
 743                 if (authorization 
!= NULL
) { 
 744                         if (authorization 
== kSCPreferencesUseEntitlementAuthorization
) { 
 745                                 astr 
= "entitlement"; 
 747                                 astr 
= "authorization"; 
 751                 SC_log(LOG_DEBUG
, "create w/%s%s%s %@", 
 753                        ((ostr 
!= "") && (astr 
!= "")) ? " + " : "", 
 758         if (authorizationData 
!= NULL
) CFRelease(authorizationData
); 
 760         return (SCPreferencesRef
)prefsPrivate
; 
 765 SCPreferencesGetTypeID(void) { 
 766         pthread_once(&initialized
, __SCPreferencesInitialize
);  /* initialize runtime */ 
 767         return __kSCPreferencesTypeID
; 
 772 prefsNotify(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
) 
 774 #pragma unused(store) 
 776         void                            (*context_release
)(const void *); 
 779         SCPreferencesNotification       notify          
= 0; 
 780         SCPreferencesRef                prefs           
= (SCPreferencesRef
)info
; 
 781         SCPreferencesPrivateRef         prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 782         SCPreferencesCallBack           rlsFunction
; 
 784         n 
= (changedKeys 
!= NULL
) ? CFArrayGetCount(changedKeys
) : 0; 
 785         for (i 
= 0; i 
< n
; i
++) { 
 788                 key 
= CFArrayGetValueAtIndex(changedKeys
, i
); 
 791                 if (CFEqual(key
, prefsPrivate
->sessionKeyCommit
)) { 
 792                         // if preferences have been saved 
 793                         notify 
|= kSCPreferencesNotificationCommit
; 
 798                 if (CFEqual(key
, prefsPrivate
->sessionKeyApply
)) { 
 799                         // if stored preferences should be applied to current configuration 
 800                         notify 
|= kSCPreferencesNotificationApply
; 
 810         pthread_mutex_lock(&prefsPrivate
->lock
); 
 813         rlsFunction 
= prefsPrivate
->rlsFunction
; 
 814         if (prefsPrivate
->rlsContext
.retain 
!= NULL
) { 
 815                 context_info    
= (void *)prefsPrivate
->rlsContext
.retain(prefsPrivate
->rlsContext
.info
); 
 816                 context_release 
= prefsPrivate
->rlsContext
.release
; 
 818                 context_info    
= prefsPrivate
->rlsContext
.info
; 
 819                 context_release 
= NULL
; 
 822         pthread_mutex_unlock(&prefsPrivate
->lock
); 
 824         if (rlsFunction 
!= NULL
) { 
 825                 SC_log(LOG_DEBUG
, "exec SCPreferences callout: %s%s%s", 
 826                        ((notify 
& kSCPreferencesNotificationCommit
) != 0) ? "commit" : "", 
 827                        (((notify 
& kSCPreferencesNotificationCommit
) != 0) && 
 828                         ((notify 
& kSCPreferencesNotificationApply 
) != 0)) ? ", " : "", 
 829                        ((notify 
& kSCPreferencesNotificationApply
)  != 0) ? "apply"  : ""); 
 830                 (*rlsFunction
)(prefs
, notify
, context_info
); 
 833         if (context_release 
!= NULL
) { 
 834                 (*context_release
)(context_info
); 
 841 __private_extern__ 
void 
 842 __SCPreferencesAddSessionKeys(SCPreferencesRef prefs
) 
 844         SCPreferencesPrivateRef         prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 846         /* create the session "commit" key */ 
 847         if (prefsPrivate
->sessionKeyCommit 
== NULL
) { 
 848                 prefsPrivate
->sessionKeyCommit 
= _SCPNotificationKey(NULL
, 
 849                                                                      prefsPrivate
->prefsID
, 
 850                                                                      kSCPreferencesKeyCommit
); 
 853         /* create the session "apply" key */ 
 854         if (prefsPrivate
->sessionKeyApply 
== NULL
) { 
 855                 prefsPrivate
->sessionKeyApply 
= _SCPNotificationKey(NULL
, 
 856                                                                     prefsPrivate
->prefsID
, 
 857                                                                     kSCPreferencesKeyApply
); 
 864 __private_extern__ Boolean
 
 865 __SCPreferencesAddSession(SCPreferencesRef prefs
) 
 867         CFAllocatorRef                  allocator       
= CFGetAllocator(prefs
); 
 868         SCDynamicStoreContext           context         
= { 0 
 874         SCPreferencesPrivateRef         prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 876         if (prefsPrivate
->sessionRefcnt 
== 0) { 
 877                 /* establish a dynamic store session */ 
 878                 prefsPrivate
->session 
= SCDynamicStoreCreate(allocator
, 
 882                 if (prefsPrivate
->session 
== NULL
) { 
 883                         SC_log(LOG_INFO
, "SCDynamicStoreCreate() failed"); 
 887                 SC_log(LOG_DEBUG
, "added SCDynamicStore session (for prefs)"); 
 890         prefsPrivate
->sessionRefcnt
++; 
 895 __private_extern__ 
void 
 896 __SCPreferencesRemoveSession(SCPreferencesRef prefs
) 
 898         SCPreferencesPrivateRef         prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 900         if (prefsPrivate
->sessionRefcnt 
> 0) { 
 901                 if (--prefsPrivate
->sessionRefcnt 
== 0) { 
 902                         CFRelease(prefsPrivate
->session
); 
 903                         prefsPrivate
->session 
= NULL
; 
 905                         SC_log(LOG_DEBUG
, "removed SCDynamicStore session (for prefs)"); 
 914 appendLockedPreferences(const void *key
, const void *value
, void *context
) 
 917         CFMutableStringRef      str     
= (CFMutableStringRef
)context
; 
 919         CFStringAppendFormat(str
, NULL
, CFSTR("%s%@"), 
 920                              (CFStringGetLength(str
) > 0) ? "\n" : "", 
 926 __private_extern__ 
void 
 927 __SCPreferencesUpdateLockedState(SCPreferencesRef prefs
, Boolean locked
) 
 929         static dispatch_queue_t         lockedQueue
; 
 930         static CFMutableDictionaryRef   lockedState
; 
 931         static dispatch_once_t          once
; 
 932         SCPreferencesPrivateRef         prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 934         dispatch_once(&once
, ^{ 
 935                 os_state_block_t        state_block
; 
 937                 lockedQueue 
= dispatch_queue_create("SCPreferences locked state queue", NULL
); 
 939                 lockedState 
= CFDictionaryCreateMutable(NULL
, 
 941                                                         NULL
,   // NO retain/release 
 942                                                         &kCFTypeDictionaryValueCallBacks
); 
 944                 state_block 
= ^os_state_data_t(os_state_hints_t hints
) { 
 945 #pragma unused(hints) 
 946                         CFDataRef               data    
= NULL
; 
 948                         os_state_data_t         state_data
; 
 949                         size_t                  state_data_size
; 
 951                         CFMutableStringRef      str
; 
 953                         if (CFDictionaryGetCount(lockedState
) == 0) { 
 954                                 // if no locked preferences 
 958                         str 
= CFStringCreateMutable(NULL
, 0); 
 959                         CFDictionaryApplyFunction(lockedState
, appendLockedPreferences
, str
); 
 960                         ok 
= _SCSerialize(str
, &data
, NULL
, NULL
); 
 963                         state_len 
= (ok 
&& (data 
!= NULL
)) ? CFDataGetLength(data
) : 0; 
 964                         state_data_size 
= OS_STATE_DATA_SIZE_NEEDED(state_len
); 
 965                         if (state_data_size 
> MAX_STATEDUMP_SIZE
) { 
 966                                 SC_log(LOG_ERR
, "locked SCPreferences : state data too large (%zd > %zd)", 
 968                                        (size_t)MAX_STATEDUMP_SIZE
); 
 969                                 if (data 
!= NULL
) CFRelease(data
); 
 973                         state_data 
= calloc(1, state_data_size
); 
 974                         if (state_data 
== NULL
) { 
 975                                 SC_log(LOG_ERR
, "locked SCPreferences: could not allocate state data"); 
 976                                 if (data 
!= NULL
) CFRelease(data
); 
 980                         state_data
->osd_type 
= OS_STATE_DATA_SERIALIZED_NSCF_OBJECT
; 
 981                         state_data
->osd_data_size 
= (uint32_t)state_len
; 
 982                         strlcpy(state_data
->osd_title
, "open/locked SCPreferences", sizeof(state_data
->osd_title
)); 
 984                                 memcpy(state_data
->osd_data
, CFDataGetBytePtr(data
), state_len
); 
 986                         if (data 
!= NULL
) CFRelease(data
); 
 991                 (void) os_state_add_handler(lockedQueue
, state_block
); 
 994         // update the locked state 
 995         prefsPrivate
->locked 
= locked
; 
 997         // add (or update) the locked preferences 
 998         dispatch_sync(lockedQueue
, ^{ 
1002                         str 
= CFCopyDescription(prefs
); 
1003                         CFDictionarySetValue(lockedState
, prefs
, str
); 
1006                         CFDictionaryRemoveValue(lockedState
, prefs
); 
1015 SCPreferencesSetCallback(SCPreferencesRef       prefs
, 
1016                          SCPreferencesCallBack  callout
, 
1017                          SCPreferencesContext   
*context
) 
1019         SCPreferencesPrivateRef prefsPrivate 
= (SCPreferencesPrivateRef
)prefs
; 
1021         if (!isA_SCPreferences(prefs
)) { 
1022                 /* sorry, you must provide a session */ 
1023                 _SCErrorSet(kSCStatusNoPrefsSession
); 
1027         pthread_mutex_lock(&prefsPrivate
->lock
); 
1029         if (prefsPrivate
->rlsContext
.release 
!= NULL
) { 
1030                 /* let go of the current context */ 
1031                 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
); 
1034         prefsPrivate
->rlsFunction                       
= callout
; 
1035         prefsPrivate
->rlsContext
.info                   
= NULL
; 
1036         prefsPrivate
->rlsContext
.retain                 
= NULL
; 
1037         prefsPrivate
->rlsContext
.release                
= NULL
; 
1038         prefsPrivate
->rlsContext
.copyDescription        
= NULL
; 
1039         if (context 
!= NULL
) { 
1040                 memcpy(&prefsPrivate
->rlsContext
, context
, sizeof(SCPreferencesContext
)); 
1041                 if (context
->retain 
!= NULL
) { 
1042                         prefsPrivate
->rlsContext
.info 
= (void *)(*context
->retain
)(context
->info
); 
1046         pthread_mutex_unlock(&prefsPrivate
->lock
); 
1053 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef     prefs
, 
1054                                    CFRunLoopRef         runLoop
, 
1055                                    CFStringRef          runLoopMode
, 
1056                                    dispatch_queue_t     queue
) 
1059         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
1061         pthread_mutex_lock(&prefsPrivate
->lock
); 
1063         if ((prefsPrivate
->dispatchQueue 
!= NULL
) ||            // if we are already scheduled on a dispatch queue 
1064             ((queue 
!= NULL
) && prefsPrivate
->scheduled
)) {     // if we are already scheduled on a CFRunLoop 
1065                 _SCErrorSet(kSCStatusInvalidArgument
); 
1069         if (!prefsPrivate
->scheduled
) { 
1070                 CFMutableArrayRef       keys
; 
1072                 // add SCDynamicStore session (for notifications) ... and hold a 'prefs' reference 
1073                 if (prefsPrivate
->session 
== NULL
) { 
1074                         ok 
= __SCPreferencesAddSession(prefs
); 
1078                         assert(prefsPrivate
->session 
!= NULL
); 
1081                 // add SCDynamicStore "keys" 
1082                 __SCPreferencesAddSessionKeys(prefs
); 
1084                 keys 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
1085                 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyCommit
); 
1086                 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyApply
); 
1087                 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, keys
, NULL
); 
1090                 if (runLoop 
!= NULL
) { 
1091                         prefsPrivate
->rls 
= SCDynamicStoreCreateRunLoopSource(NULL
, prefsPrivate
->session
, 0); 
1092                         prefsPrivate
->rlList 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
1095                 SC_log(LOG_DEBUG
, "scheduled"); 
1097                 prefsPrivate
->scheduled 
= TRUE
; 
1100         if (queue 
!= NULL
) { 
1101                 ok 
= SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, queue
); 
1103                         prefsPrivate
->scheduled 
= FALSE
; 
1104                         (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
); 
1105                         __SCPreferencesRemoveSession(prefs
); 
1109                 prefsPrivate
->dispatchQueue 
= queue
; 
1110                 dispatch_retain(prefsPrivate
->dispatchQueue
); 
1112                 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) { 
1114                          * if we do not already have notifications scheduled with 
1115                          * this runLoop / runLoopMode 
1117                         CFRunLoopAddSource(runLoop
, prefsPrivate
->rls
, runLoopMode
); 
1120                 _SC_schedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
); 
1127         pthread_mutex_unlock(&prefsPrivate
->lock
); 
1133 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef   prefs
, 
1134                                      CFRunLoopRef       runLoop
, 
1135                                      CFStringRef        runLoopMode
) 
1137         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
1141         pthread_mutex_lock(&prefsPrivate
->lock
); 
1143         if ((runLoop 
!= NULL
) && !prefsPrivate
->scheduled
) {                    // if we should be scheduled (but are not) 
1144                 _SCErrorSet(kSCStatusInvalidArgument
); 
1148         if (((runLoop 
== NULL
) && (prefsPrivate
->dispatchQueue 
== NULL
)) ||     // if we should be scheduled on a dispatch queue (but are not) 
1149             ((runLoop 
!= NULL
) && (prefsPrivate
->dispatchQueue 
!= NULL
))) {     // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue) 
1150                 _SCErrorSet(kSCStatusInvalidArgument
); 
1154         if (runLoop 
== NULL
) { 
1155                 SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, NULL
); 
1156                 dispatch_release(prefsPrivate
->dispatchQueue
); 
1157                 prefsPrivate
->dispatchQueue 
= NULL
; 
1159                 if (!_SC_unschedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
, FALSE
)) { 
1160                         // if not currently scheduled on this runLoop / runLoopMode 
1161                         _SCErrorSet(kSCStatusInvalidArgument
); 
1165                 n 
= CFArrayGetCount(prefsPrivate
->rlList
); 
1166                 if (n 
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) { 
1168                          * if we are no longer scheduled to receive notifications for 
1169                          * this runLoop / runLoopMode 
1171                         CFRunLoopRemoveSource(runLoop
, prefsPrivate
->rls
, runLoopMode
); 
1174                                 // if *all* notifications have been unscheduled 
1175                                 CFRelease(prefsPrivate
->rlList
); 
1176                                 prefsPrivate
->rlList 
= NULL
; 
1177                                 CFRunLoopSourceInvalidate(prefsPrivate
->rls
); 
1178                                 CFRelease(prefsPrivate
->rls
); 
1179                                 prefsPrivate
->rls 
= NULL
; 
1185                 CFArrayRef      changedKeys
; 
1187                 SC_log(LOG_DEBUG
, "unscheduled"); 
1189                 // if *all* notifications have been unscheduled 
1190                 prefsPrivate
->scheduled 
= FALSE
; 
1192                 // no need to track changes 
1193                 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
); 
1195                 // clear out any pending notifications 
1196                 changedKeys 
= SCDynamicStoreCopyNotifiedKeys(prefsPrivate
->session
); 
1197                 if (changedKeys 
!= NULL
) { 
1198                         CFRelease(changedKeys
); 
1201                 // remove SCDynamicStore session, release 'prefs' reference 
1202                 __SCPreferencesRemoveSession(prefs
); 
1209         pthread_mutex_unlock(&prefsPrivate
->lock
); 
1215 SCPreferencesScheduleWithRunLoop(SCPreferencesRef       prefs
, 
1216                                  CFRunLoopRef           runLoop
, 
1217                                  CFStringRef            runLoopMode
) 
1219         if (!isA_SCPreferences(prefs
) || (runLoop 
== NULL
) || (runLoopMode 
== NULL
)) { 
1220                 _SCErrorSet(kSCStatusInvalidArgument
); 
1224         return __SCPreferencesScheduleWithRunLoop(prefs
, runLoop
, runLoopMode
, NULL
); 
1229 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef     prefs
, 
1230                                    CFRunLoopRef         runLoop
, 
1231                                    CFStringRef          runLoopMode
) 
1233         if (!isA_SCPreferences(prefs
) || (runLoop 
== NULL
) || (runLoopMode 
== NULL
)) { 
1234                 _SCErrorSet(kSCStatusInvalidArgument
); 
1238         return __SCPreferencesUnscheduleFromRunLoop(prefs
, runLoop
, runLoopMode
); 
1243 SCPreferencesSetDispatchQueue(SCPreferencesRef  prefs
, 
1244                               dispatch_queue_t  queue
) 
1248         if (!isA_SCPreferences(prefs
)) { 
1249                 /* sorry, you must provide a session */ 
1250                 _SCErrorSet(kSCStatusNoPrefsSession
); 
1254         if (queue 
!= NULL
) { 
1255                 ok 
= __SCPreferencesScheduleWithRunLoop(prefs
, NULL
, NULL
, queue
); 
1257                 ok 
= __SCPreferencesUnscheduleFromRunLoop(prefs
, NULL
, NULL
); 
1265 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs
) 
1268         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
1269         uint32_t                status          
= kSCStatusOK
; 
1271         if (prefsPrivate
->helper_port 
== MACH_PORT_NULL
) { 
1276         // have the helper "synchronize" the prefs 
1277         ok 
= _SCHelperExec(prefsPrivate
->helper_port
, 
1278                            SCHELPER_MSG_PREFS_SYNCHRONIZE
, 
1284                 if (prefsPrivate
->helper_port 
!= MACH_PORT_NULL
) { 
1285                         _SCHelperClose(&prefsPrivate
->helper_port
); 
1294 SCPreferencesSynchronize(SCPreferencesRef prefs
) 
1296         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
1298         if (!isA_SCPreferences(prefs
)) { 
1299                 /* sorry, you must provide a session */ 
1300                 _SCErrorSet(kSCStatusNoPrefsSession
); 
1304         SC_log(LOG_DEBUG
, "SCPreferences() synchronize: %s", 
1305                prefsPrivate
->newPath 
? prefsPrivate
->newPath 
: prefsPrivate
->path
); 
1307         if (prefsPrivate
->authorizationData 
!= NULL
) { 
1308                 __SCPreferencesSynchronize_helper(prefs
); 
1310         if (prefsPrivate
->prefs 
!= NULL
) { 
1311                 CFRelease(prefsPrivate
->prefs
); 
1312                 prefsPrivate
->prefs 
= NULL
; 
1314         if (prefsPrivate
->signature 
!= NULL
) { 
1315                 CFRelease(prefsPrivate
->signature
); 
1316                 prefsPrivate
->signature 
= NULL
; 
1318         prefsPrivate
->accessed 
= FALSE
; 
1319         prefsPrivate
->changed  
= FALSE
;