2 * Copyright(c) 2000-2020 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");
69 __SCPreferencesCopyDescription(CFTypeRef cf
) {
70 CFAllocatorRef allocator
= CFGetAllocator(cf
);
71 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)cf
;
72 CFMutableStringRef result
;
74 result
= CFStringCreateMutable(allocator
, 0);
75 CFStringAppendFormat(result
, NULL
, CFSTR("<SCPreferences %p [%p]> {"), cf
, allocator
);
76 CFStringAppendFormat(result
, NULL
, CFSTR("name = %@"), prefsPrivate
->name
);
77 CFStringAppendFormat(result
, NULL
, CFSTR(", id = %@"),
78 prefsPrivate
->prefsID
!= NULL
? prefsPrivate
->prefsID
: CFSTR("[default]"));
79 CFStringAppendFormat(result
, NULL
, CFSTR(", path = %s"),
80 prefsPrivate
->newPath
!= NULL
? prefsPrivate
->newPath
: prefsPrivate
->path
);
81 if (prefsPrivate
->accessed
) {
82 CFStringAppendFormat(result
, NULL
, CFSTR(", accessed"));
84 if (prefsPrivate
->changed
) {
85 CFStringAppendFormat(result
, NULL
, CFSTR(", changed"));
87 if (prefsPrivate
->locked
) {
88 CFStringAppendFormat(result
, NULL
, CFSTR(", locked"));
90 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
91 CFStringAppendFormat(result
, NULL
, CFSTR(", helper port = 0x%x"), prefsPrivate
->helper_port
);
93 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
100 __SCPreferencesDeallocate(CFTypeRef cf
)
102 SCPreferencesRef prefs
= (SCPreferencesRef
)cf
;
103 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
105 SC_log(LOG_DEBUG
, "release %@", prefsPrivate
);
107 if (prefsPrivate
->locked
) {
108 __SCPreferencesUpdateLockedState(prefs
, FALSE
);
111 /* release resources */
113 pthread_mutex_destroy(&prefsPrivate
->lock
);
115 if (prefsPrivate
->parent
!= NULL
) {
116 SCPreferencesPrivateRef parentPrivate
= (SCPreferencesPrivateRef
)prefsPrivate
->parent
;
118 // remove [weak] reference from parent to this companion
119 pthread_mutex_lock(&parentPrivate
->lock
);
120 CFDictionaryRemoveValue(parentPrivate
->companions
, prefsPrivate
->prefsID
);
121 pthread_mutex_unlock(&parentPrivate
->lock
);
123 // remove [strong] reference from companion to parent
124 CFRelease(prefsPrivate
->parent
);
126 if (prefsPrivate
->companions
!= NULL
) CFRelease(prefsPrivate
->companions
);
128 if (prefsPrivate
->name
) CFRelease(prefsPrivate
->name
);
129 if (prefsPrivate
->prefsID
) CFRelease(prefsPrivate
->prefsID
);
130 if (prefsPrivate
->options
) CFRelease(prefsPrivate
->options
);
131 if (prefsPrivate
->path
) CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
132 if (prefsPrivate
->newPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->newPath
);
133 if (prefsPrivate
->lockFD
!= -1) {
134 if (prefsPrivate
->lockPath
!= NULL
) {
135 unlink(prefsPrivate
->lockPath
);
137 close(prefsPrivate
->lockFD
);
139 if (prefsPrivate
->lockPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->lockPath
);
140 if (prefsPrivate
->signature
) CFRelease(prefsPrivate
->signature
);
141 if (prefsPrivate
->sessionNoO_EXLOCK
!= NULL
) {
142 CFRelease(prefsPrivate
->sessionNoO_EXLOCK
);
144 if (prefsPrivate
->sessionKeyLock
) CFRelease(prefsPrivate
->sessionKeyLock
);
145 if (prefsPrivate
->sessionKeyCommit
) CFRelease(prefsPrivate
->sessionKeyCommit
);
146 if (prefsPrivate
->sessionKeyApply
) CFRelease(prefsPrivate
->sessionKeyApply
);
147 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
148 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
150 if (prefsPrivate
->prefs
) CFRelease(prefsPrivate
->prefs
);
151 if (prefsPrivate
->authorizationData
!= NULL
) CFRelease(prefsPrivate
->authorizationData
);
152 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
153 (void) _SCHelperExec(prefsPrivate
->helper_port
,
154 SCHELPER_MSG_PREFS_CLOSE
,
158 _SCHelperClose(&prefsPrivate
->helper_port
);
165 static CFTypeID __kSCPreferencesTypeID
= _kCFRuntimeNotATypeID
;
168 static const CFRuntimeClass __SCPreferencesClass
= {
170 "SCPreferences", // className
173 __SCPreferencesDeallocate
, // dealloc
176 NULL
, // copyFormattingDesc
177 __SCPreferencesCopyDescription
// copyDebugDesc
181 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
184 __SCPreferencesInitialize(void) {
185 /* register with CoreFoundation */
186 __kSCPreferencesTypeID
= _CFRuntimeRegisterClass(&__SCPreferencesClass
);
191 static SCPreferencesPrivateRef
192 __SCPreferencesCreatePrivate(CFAllocatorRef allocator
)
194 SCPreferencesPrivateRef prefsPrivate
;
197 /* initialize runtime */
198 pthread_once(&initialized
, __SCPreferencesInitialize
);
200 /* allocate prefs session */
201 size
= sizeof(SCPreferencesPrivate
) - sizeof(CFRuntimeBase
);
202 prefsPrivate
= (SCPreferencesPrivateRef
)_CFRuntimeCreateInstance(allocator
,
203 __kSCPreferencesTypeID
,
206 if (prefsPrivate
== NULL
) {
210 /* initialize non-zero/NULL members */
211 pthread_mutex_init(&prefsPrivate
->lock
, NULL
);
212 prefsPrivate
->lockFD
= -1;
213 prefsPrivate
->isRoot
= (geteuid() == 0);
219 __private_extern__ Boolean
220 __SCPreferencesCreate_helper(SCPreferencesRef prefs
)
222 CFDataRef data
= NULL
;
223 CFMutableDictionaryRef info
;
226 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
227 uint32_t status
= kSCStatusOK
;
229 uint32_t pid
= getpid();
232 ok
= _SCHelperOpen(prefsPrivate
->authorizationData
,
233 &prefsPrivate
->helper_port
);
238 // create a dictionary of information to pass to the helper
239 info
= CFDictionaryCreateMutable(NULL
,
241 &kCFTypeDictionaryKeyCallBacks
,
242 &kCFTypeDictionaryValueCallBacks
);
245 if (prefsPrivate
->prefsID
!= NULL
) {
246 CFDictionarySetValue(info
, CFSTR("prefsID"), prefsPrivate
->prefsID
);
250 if (prefsPrivate
->options
!= NULL
) {
251 CFDictionarySetValue(info
, CFSTR("options"), prefsPrivate
->options
);
254 // save preferences session "name"
255 CFDictionarySetValue(info
, CFSTR("name"), prefsPrivate
->name
);
258 num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &pid
);
259 CFDictionarySetValue(info
, CFSTR("PID"), num
);
263 str
= CFStringCreateWithCString(NULL
, getprogname(), kCFStringEncodingUTF8
);
264 CFDictionarySetValue(info
, CFSTR("PROC_NAME"), str
);
267 // serialize the info
268 ok
= _SCSerialize(info
, &data
, NULL
, NULL
);
270 if (data
== NULL
|| !ok
) {
274 // have the helper "open" the prefs
275 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
276 SCHELPER_MSG_PREFS_OPEN
,
280 if (data
!= NULL
) CFRelease(data
);
285 if (status
!= kSCStatusOK
) {
294 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
295 _SCHelperClose(&prefsPrivate
->helper_port
);
298 status
= kSCStatusAccessError
;
309 __SCPreferencesAccess_helper(SCPreferencesRef prefs
)
312 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
313 CFDictionaryRef serverDict
= NULL
;
314 CFDictionaryRef serverPrefs
= NULL
;
315 CFDictionaryRef serverSignature
= NULL
;
316 uint32_t status
= kSCStatusOK
;
317 CFDataRef reply
= NULL
;
319 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
320 ok
= __SCPreferencesCreate_helper(prefs
);
326 // have the helper "access" the prefs
327 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
328 SCHELPER_MSG_PREFS_ACCESS
,
336 if (status
!= kSCStatusOK
) {
344 ok
= _SCUnserialize((CFPropertyListRef
*)&serverDict
, reply
, NULL
, 0);
350 if (isA_CFDictionary(serverDict
)) {
351 serverPrefs
= CFDictionaryGetValue(serverDict
, CFSTR("preferences"));
352 serverPrefs
= isA_CFDictionary(serverPrefs
);
354 serverSignature
= CFDictionaryGetValue(serverDict
, CFSTR("signature"));
355 serverSignature
= isA_CFData(serverSignature
);
358 if ((serverPrefs
== NULL
) || (serverSignature
== NULL
)) {
359 if (serverDict
!= NULL
) CFRelease(serverDict
);
363 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, serverPrefs
);
364 prefsPrivate
->signature
= CFRetain(serverSignature
);
365 prefsPrivate
->accessed
= TRUE
;
366 CFRelease(serverDict
);
373 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
374 _SCHelperClose(&prefsPrivate
->helper_port
);
377 status
= kSCStatusAccessError
;
387 static SCPreferencesPrivateRef
388 __SCPreferencesCreate(CFAllocatorRef allocator
,
391 CFDataRef authorizationData
,
392 CFDictionaryRef options
)
394 SCPreferencesPrivateRef prefsPrivate
;
395 int sc_status
= kSCStatusOK
;
398 * allocate and initialize a new prefs session
400 prefsPrivate
= __SCPreferencesCreatePrivate(allocator
);
401 if (prefsPrivate
== NULL
) {
405 prefsPrivate
->name
= CFStringCreateCopy(allocator
, name
);
406 if (prefsID
!= NULL
) {
407 prefsPrivate
->prefsID
= CFStringCreateCopy(allocator
, prefsID
);
409 if (authorizationData
!= NULL
) {
410 prefsPrivate
->authorizationData
= CFRetain(authorizationData
);
412 if (options
!= NULL
) {
413 prefsPrivate
->options
= CFDictionaryCreateCopy(allocator
, options
);
419 * convert prefsID to path
421 prefsPrivate
->path
= __SCPreferencesPath(allocator
,
423 (prefsPrivate
->newPath
== NULL
));
424 if (prefsPrivate
->path
== NULL
) {
425 sc_status
= kSCStatusFailed
;
429 if (access(prefsPrivate
->path
, R_OK
) == 0) {
436 if ((prefsID
== NULL
) || !CFStringHasPrefix(prefsID
, CFSTR("/"))) {
437 /* if default preference ID or relative path */
438 if (prefsPrivate
->newPath
== NULL
) {
440 * we've looked in the "new" prefs directory
441 * without success. Save the "new" path and
442 * look in the "old" prefs directory.
444 prefsPrivate
->newPath
= prefsPrivate
->path
;
448 * we've looked in both the "new" and "old"
449 * prefs directories without success. Use
452 CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
453 prefsPrivate
->path
= prefsPrivate
->newPath
;
454 prefsPrivate
->newPath
= NULL
;
458 /* no preference data, start fresh */
459 sc_status
= kSCStatusNoConfigFile
;
463 if (prefsPrivate
->authorizationData
!= NULL
) {
464 /* no problem, we'll be using the helper */
468 SC_log(LOG_NOTICE
, "open() failed: %s", strerror(errno
));
469 sc_status
= kSCStatusAccessError
;
472 SC_log(LOG_NOTICE
, "open() failed: %s", strerror(errno
));
473 sc_status
= kSCStatusFailed
;
479 CFRelease(prefsPrivate
);
480 _SCErrorSet(sc_status
);
486 _SCErrorSet(sc_status
);
491 __private_extern__
void
492 __SCPreferencesAccess(SCPreferencesRef prefs
)
494 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
496 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
499 if (prefsPrivate
->accessed
) {
500 // if preference data has already been accessed
504 if (access(prefsPrivate
->path
, R_OK
) == 0) {
505 fd
= open(prefsPrivate
->path
, O_RDONLY
, 0644);
511 if (fstat(fd
, &statBuf
) == -1) {
512 SC_log(LOG_NOTICE
, "fstat() failed: %s", strerror(errno
));
513 memset(&statBuf
, 0, sizeof(statBuf
));
518 /* no preference data, start fresh */
522 if (prefsPrivate
->authorizationData
!= NULL
) {
523 if (__SCPreferencesAccess_helper(prefs
)) {
526 SC_log(LOG_NOTICE
, "__SCPreferencesAccess_helper() failed: %s",
527 SCErrorString(SCError()));
533 SC_log(LOG_NOTICE
, "open() failed: %s", strerror(errno
));
536 memset(&statBuf
, 0, sizeof(statBuf
));
539 if (prefsPrivate
->signature
!= NULL
) CFRelease(prefsPrivate
->signature
);
540 prefsPrivate
->signature
= __SCPSignatureFromStatbuf(&statBuf
);
542 if (statBuf
.st_size
> 0) {
543 CFDictionaryRef dict
;
544 CFErrorRef error
= NULL
;
545 CFMutableDataRef xmlData
;
548 * extract property list
550 xmlData
= CFDataCreateMutable(allocator
, (CFIndex
)statBuf
.st_size
);
551 CFDataSetLength(xmlData
, (CFIndex
)statBuf
.st_size
);
552 if (read(fd
, (void *)CFDataGetBytePtr(xmlData
), (CFIndex
)statBuf
.st_size
) != (CFIndex
)statBuf
.st_size
) {
553 /* corrupt prefs file, start fresh */
554 SC_log(LOG_INFO
, "read(): could not load preference data");
563 dict
= CFPropertyListCreateWithData(allocator
, xmlData
, kCFPropertyListImmutable
, NULL
, &error
);
566 /* corrupt prefs file, start fresh */
568 SC_log(LOG_NOTICE
, "CFPropertyListCreateWithData(): %@", error
);
575 * make sure that we've got a dictionary
577 if (!isA_CFDictionary(dict
)) {
578 /* corrupt prefs file, start fresh */
579 SC_log(LOG_INFO
, "CFGetTypeID(): not a dictionary");
584 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(allocator
, 0, dict
);
594 if (prefsPrivate
->prefs
== NULL
) {
596 * new file, create empty preferences
598 // SC_log(LOG_INFO, "creating new preferences file");
599 prefsPrivate
->prefs
= CFDictionaryCreateMutable(allocator
,
601 &kCFTypeDictionaryKeyCallBacks
,
602 &kCFTypeDictionaryValueCallBacks
);
603 prefsPrivate
->changed
= TRUE
;
606 SC_log(LOG_DEBUG
, "SCPreferences() access: %s, size=%lld",
607 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
,
608 __SCPreferencesPrefsSize(prefs
));
610 prefsPrivate
->accessed
= TRUE
;
616 SCPreferencesCreate(CFAllocatorRef allocator
,
620 SCPreferencesPrivateRef prefsPrivate
;
622 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, NULL
, NULL
);
623 if (prefsPrivate
!= NULL
) {
624 SC_log(LOG_DEBUG
, "create %@", prefsPrivate
);
627 return (SCPreferencesRef
)prefsPrivate
;
632 SCPreferencesCreateWithAuthorization(CFAllocatorRef allocator
,
635 AuthorizationRef authorization
)
637 SCPreferencesRef prefs
;
639 #if !TARGET_OS_IPHONE
640 if (authorization
== NULL
) {
641 authorization
= kSCPreferencesUseEntitlementAuthorization
;
643 #else // !TARGET_OS_IPHONE
644 authorization
= kSCPreferencesUseEntitlementAuthorization
;
645 #endif // !TARGET_OS_IPHONE
647 prefs
= SCPreferencesCreateWithOptions(allocator
, name
, prefsID
, authorization
, NULL
);
653 SCPreferencesCreateWithOptions(CFAllocatorRef allocator
,
656 AuthorizationRef authorization
,
657 CFDictionaryRef options
)
659 CFDataRef authorizationData
= NULL
;
660 SCPreferencesPrivateRef prefsPrivate
;
662 if (options
!= NULL
) {
663 if (!isA_CFDictionary(options
)) {
664 _SCErrorSet(kSCStatusInvalidArgument
);
669 if (authorization
!= NULL
) {
670 CFMutableDictionaryRef authorizationDict
;
671 CFStringRef bundleID
;
673 authorizationDict
= CFDictionaryCreateMutable(NULL
,
675 &kCFTypeDictionaryKeyCallBacks
,
676 &kCFTypeDictionaryValueCallBacks
);
677 #if !TARGET_OS_IPHONE
678 if (authorization
!= kSCPreferencesUseEntitlementAuthorization
) {
680 AuthorizationExternalForm extForm
;
683 os_status
= AuthorizationMakeExternalForm(authorization
, &extForm
);
684 if (os_status
!= errAuthorizationSuccess
) {
685 SC_log(LOG_INFO
, "AuthorizationMakeExternalForm() failed");
686 _SCErrorSet(kSCStatusInvalidArgument
);
687 CFRelease(authorizationDict
);
691 data
= CFDataCreate(NULL
, (const UInt8
*)extForm
.bytes
, sizeof(extForm
.bytes
));
692 CFDictionaryAddValue(authorizationDict
,
693 kSCHelperAuthAuthorization
,
697 #endif // !TARGET_OS_IPHONE
699 bundleID
= _SC_getApplicationBundleID();
700 CFDictionaryAddValue(authorizationDict
,
701 kSCHelperAuthCallerInfo
,
704 if (authorizationDict
!= NULL
) {
705 (void) _SCSerialize((CFPropertyListRef
)authorizationDict
,
709 CFRelease(authorizationDict
);
713 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, authorizationData
, options
);
714 if (prefsPrivate
!= NULL
) {
715 const char *astr
= "";
716 const char *ostr
= "";
718 if (options
!= NULL
) {
722 if (authorization
!= NULL
) {
723 if (authorization
== kSCPreferencesUseEntitlementAuthorization
) {
724 astr
= "entitlement";
726 astr
= "authorization";
730 SC_log(LOG_DEBUG
, "create w/%s%s%s %@",
732 ((ostr
!= "") && (astr
!= "")) ? " + " : "",
737 if (authorizationData
!= NULL
) CFRelease(authorizationData
);
739 return (SCPreferencesRef
)prefsPrivate
;
744 SCPreferencesCreateCompanion(SCPreferencesRef prefs
, CFStringRef companionPrefsID
)
746 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
747 SCPreferencesPrivateRef companionPrefs
= NULL
;
748 CFMutableStringRef newPrefsID
;
749 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
751 if (companionPrefsID
== NULL
) {
752 companionPrefsID
= PREFS_DEFAULT_CONFIG
;
754 if (CFStringFindWithOptions(companionPrefsID
,
756 CFRangeMake(0, CFStringGetLength(companionPrefsID
)),
759 // if companion prefsID contains a "/"
760 _SCErrorSet(kSCStatusInvalidArgument
);
765 if (prefsPrivate
->prefsID
== NULL
) {
766 if (CFEqual(companionPrefsID
, PREFS_DEFAULT_CONFIG
)) {
767 // if prefsID and companionPrefsID match
768 _SCErrorSet(kSCStatusInvalidArgument
);
771 newPrefsID
= CFStringCreateMutableCopy(allocator
, 0, companionPrefsID
);
773 CFIndex prefsIDLen
= CFStringGetLength(prefsPrivate
->prefsID
);
776 if (CFStringFindWithOptions(prefsPrivate
->prefsID
,
778 CFRangeMake(0, prefsIDLen
),
784 // if slash, check suffix
786 if (range
.location
>= prefsIDLen
) {
788 _SCErrorSet(kSCStatusInvalidArgument
);
791 range
.length
= prefsIDLen
- range
.location
;
792 suffix
= CFStringCreateWithSubstring(allocator
, prefsPrivate
->prefsID
, range
);
793 match
= CFEqual(suffix
, companionPrefsID
);
796 // if prefsID [suffix] and companionPrefsID match
797 _SCErrorSet(kSCStatusInvalidArgument
);
801 // replace the suffix
802 newPrefsID
= CFStringCreateMutableCopy(NULL
, 0, prefsPrivate
->prefsID
);
803 CFStringReplace(newPrefsID
, range
, companionPrefsID
);
804 } else if (!CFEqual(prefsPrivate
->prefsID
, companionPrefsID
)) {
805 // if no slash, prefsID and companionPrefsID differ
806 newPrefsID
= CFStringCreateMutableCopy(NULL
, 0, companionPrefsID
);
808 // if no slash, prefsID and companionPrefsID match
809 _SCErrorSet(kSCStatusInvalidArgument
);
813 assert(newPrefsID
!= NULL
);
815 pthread_mutex_lock(&prefsPrivate
->lock
);
816 if ((prefsPrivate
->companions
!= NULL
) &&
817 CFDictionaryGetValueIfPresent(prefsPrivate
->companions
,
819 (const void **)&companionPrefs
) &&
820 (companionPrefs
!= NULL
)) {
821 // if we already have a companion
822 SC_log(LOG_DEBUG
, "create [companion] reference %@", companionPrefs
);
823 CFRetain(companionPrefs
);
825 companionPrefs
= __SCPreferencesCreate(allocator
,
828 prefsPrivate
->authorizationData
,
829 prefsPrivate
->options
);
830 if (companionPrefs
!= NULL
) {
831 SCPreferencesPrivateRef companionPrefsPrivate
= (SCPreferencesPrivateRef
)companionPrefs
;
833 SC_log(LOG_DEBUG
, "create [companion] %@", companionPrefs
);
835 // add [strong] reference from companion to parent
836 companionPrefsPrivate
->parent
= CFRetain(prefs
);
838 // add [weak] reference from parent to this companion
839 if (prefsPrivate
->companions
== NULL
) {
840 prefsPrivate
->companions
= CFDictionaryCreateMutable(NULL
,
842 &kCFTypeDictionaryKeyCallBacks
,
845 CFDictionarySetValue(prefsPrivate
->companions
, newPrefsID
, companionPrefs
);
848 pthread_mutex_unlock(&prefsPrivate
->lock
);
850 CFRelease(newPrefsID
);
852 return (SCPreferencesRef
)companionPrefs
;
857 SCPreferencesGetTypeID(void) {
858 pthread_once(&initialized
, __SCPreferencesInitialize
); /* initialize runtime */
859 return __kSCPreferencesTypeID
;
864 prefsNotify(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
866 #pragma unused(store)
868 void (*context_release
)(const void *);
871 SCPreferencesNotification notify
= 0;
872 SCPreferencesRef prefs
= (SCPreferencesRef
)info
;
873 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
874 SCPreferencesCallBack rlsFunction
;
876 n
= (changedKeys
!= NULL
) ? CFArrayGetCount(changedKeys
) : 0;
877 for (i
= 0; i
< n
; i
++) {
880 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
883 if (CFEqual(key
, prefsPrivate
->sessionKeyCommit
)) {
884 // if preferences have been saved
885 notify
|= kSCPreferencesNotificationCommit
;
890 if (CFEqual(key
, prefsPrivate
->sessionKeyApply
)) {
891 // if stored preferences should be applied to current configuration
892 notify
|= kSCPreferencesNotificationApply
;
902 pthread_mutex_lock(&prefsPrivate
->lock
);
905 rlsFunction
= prefsPrivate
->rlsFunction
;
906 if (prefsPrivate
->rlsContext
.retain
!= NULL
) {
907 context_info
= (void *)prefsPrivate
->rlsContext
.retain(prefsPrivate
->rlsContext
.info
);
908 context_release
= prefsPrivate
->rlsContext
.release
;
910 context_info
= prefsPrivate
->rlsContext
.info
;
911 context_release
= NULL
;
914 pthread_mutex_unlock(&prefsPrivate
->lock
);
916 if (rlsFunction
!= NULL
) {
917 SC_log(LOG_DEBUG
, "exec SCPreferences callout: %s%s%s",
918 ((notify
& kSCPreferencesNotificationCommit
) != 0) ? "commit" : "",
919 (((notify
& kSCPreferencesNotificationCommit
) != 0) &&
920 ((notify
& kSCPreferencesNotificationApply
) != 0)) ? ", " : "",
921 ((notify
& kSCPreferencesNotificationApply
) != 0) ? "apply" : "");
922 (*rlsFunction
)(prefs
, notify
, context_info
);
925 if (context_release
!= NULL
) {
926 (*context_release
)(context_info
);
933 __private_extern__
void
934 __SCPreferencesAddSessionKeys(SCPreferencesRef prefs
)
936 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
938 /* create the session "commit" key */
939 if (prefsPrivate
->sessionKeyCommit
== NULL
) {
940 prefsPrivate
->sessionKeyCommit
= _SCPNotificationKey(NULL
,
941 prefsPrivate
->prefsID
,
942 kSCPreferencesKeyCommit
);
945 /* create the session "apply" key */
946 if (prefsPrivate
->sessionKeyApply
== NULL
) {
947 prefsPrivate
->sessionKeyApply
= _SCPNotificationKey(NULL
,
948 prefsPrivate
->prefsID
,
949 kSCPreferencesKeyApply
);
956 __private_extern__ Boolean
957 __SCPreferencesAddSession(SCPreferencesRef prefs
)
959 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
960 SCDynamicStoreContext context
= { 0
966 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
968 if (prefsPrivate
->sessionRefcnt
== 0) {
969 /* establish a dynamic store session */
970 prefsPrivate
->session
= SCDynamicStoreCreate(allocator
,
974 if (prefsPrivate
->session
== NULL
) {
975 SC_log(LOG_INFO
, "SCDynamicStoreCreate() failed");
979 SC_log(LOG_DEBUG
, "added SCDynamicStore session (for prefs)");
982 prefsPrivate
->sessionRefcnt
++;
987 __private_extern__
void
988 __SCPreferencesRemoveSession(SCPreferencesRef prefs
)
990 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
992 if (prefsPrivate
->sessionRefcnt
> 0) {
993 if (--prefsPrivate
->sessionRefcnt
== 0) {
994 CFRelease(prefsPrivate
->session
);
995 prefsPrivate
->session
= NULL
;
997 SC_log(LOG_DEBUG
, "removed SCDynamicStore session (for prefs)");
1006 appendLockedPreferences(const void *key
, const void *value
, void *context
)
1009 CFMutableStringRef str
= (CFMutableStringRef
)context
;
1011 CFStringAppendFormat(str
, NULL
, CFSTR("%s%@"),
1012 (CFStringGetLength(str
) > 0) ? "\n" : "",
1018 __private_extern__
void
1019 __SCPreferencesUpdateLockedState(SCPreferencesRef prefs
, Boolean locked
)
1021 static dispatch_queue_t lockedQueue
;
1022 static CFMutableDictionaryRef lockedState
;
1023 static dispatch_once_t once
;
1024 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1026 dispatch_once(&once
, ^{
1027 os_state_block_t state_block
;
1029 lockedQueue
= dispatch_queue_create("SCPreferences locked state queue", NULL
);
1031 lockedState
= CFDictionaryCreateMutable(NULL
,
1033 NULL
, // NO retain/release
1034 &kCFTypeDictionaryValueCallBacks
);
1036 state_block
= ^os_state_data_t(os_state_hints_t hints
) {
1037 #pragma unused(hints)
1038 CFDataRef data
= NULL
;
1040 os_state_data_t state_data
;
1041 size_t state_data_size
;
1043 CFMutableStringRef str
;
1045 if (CFDictionaryGetCount(lockedState
) == 0) {
1046 // if no locked preferences
1050 str
= CFStringCreateMutable(NULL
, 0);
1051 CFDictionaryApplyFunction(lockedState
, appendLockedPreferences
, str
);
1052 ok
= _SCSerialize(str
, &data
, NULL
, NULL
);
1055 state_len
= (ok
&& (data
!= NULL
)) ? CFDataGetLength(data
) : 0;
1056 state_data_size
= OS_STATE_DATA_SIZE_NEEDED(state_len
);
1057 if (state_data_size
> MAX_STATEDUMP_SIZE
) {
1058 SC_log(LOG_ERR
, "locked SCPreferences : state data too large (%zd > %zd)",
1060 (size_t)MAX_STATEDUMP_SIZE
);
1061 if (data
!= NULL
) CFRelease(data
);
1065 state_data
= calloc(1, state_data_size
);
1066 if (state_data
== NULL
) {
1067 SC_log(LOG_ERR
, "locked SCPreferences: could not allocate state data");
1068 if (data
!= NULL
) CFRelease(data
);
1072 state_data
->osd_type
= OS_STATE_DATA_SERIALIZED_NSCF_OBJECT
;
1073 state_data
->osd_data_size
= (uint32_t)state_len
;
1074 strlcpy(state_data
->osd_title
, "open/locked SCPreferences", sizeof(state_data
->osd_title
));
1075 if (state_len
> 0) {
1076 memcpy(state_data
->osd_data
, CFDataGetBytePtr(data
), state_len
);
1078 if (data
!= NULL
) CFRelease(data
);
1083 (void) os_state_add_handler(lockedQueue
, state_block
);
1086 // update the locked state
1087 prefsPrivate
->locked
= locked
;
1089 // add (or update) the locked preferences
1090 dispatch_sync(lockedQueue
, ^{
1094 str
= CFCopyDescription(prefs
);
1095 CFDictionarySetValue(lockedState
, prefs
, str
);
1098 CFDictionaryRemoveValue(lockedState
, prefs
);
1107 SCPreferencesSetCallback(SCPreferencesRef prefs
,
1108 SCPreferencesCallBack callout
,
1109 SCPreferencesContext
*context
)
1111 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1113 if (!isA_SCPreferences(prefs
)) {
1114 /* sorry, you must provide a session */
1115 _SCErrorSet(kSCStatusNoPrefsSession
);
1119 pthread_mutex_lock(&prefsPrivate
->lock
);
1121 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
1122 /* let go of the current context */
1123 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
1126 prefsPrivate
->rlsFunction
= callout
;
1127 prefsPrivate
->rlsContext
.info
= NULL
;
1128 prefsPrivate
->rlsContext
.retain
= NULL
;
1129 prefsPrivate
->rlsContext
.release
= NULL
;
1130 prefsPrivate
->rlsContext
.copyDescription
= NULL
;
1131 if (context
!= NULL
) {
1132 memcpy(&prefsPrivate
->rlsContext
, context
, sizeof(SCPreferencesContext
));
1133 if (context
->retain
!= NULL
) {
1134 prefsPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
1138 pthread_mutex_unlock(&prefsPrivate
->lock
);
1145 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
1146 CFRunLoopRef runLoop
,
1147 CFStringRef runLoopMode
,
1148 dispatch_queue_t queue
)
1151 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1153 pthread_mutex_lock(&prefsPrivate
->lock
);
1155 if ((prefsPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
1156 ((queue
!= NULL
) && prefsPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
1157 _SCErrorSet(kSCStatusInvalidArgument
);
1161 if (!prefsPrivate
->scheduled
) {
1162 CFMutableArrayRef keys
;
1164 // add SCDynamicStore session (for notifications) ... and hold a 'prefs' reference
1165 if (prefsPrivate
->session
== NULL
) {
1166 ok
= __SCPreferencesAddSession(prefs
);
1170 assert(prefsPrivate
->session
!= NULL
);
1173 // add SCDynamicStore "keys"
1174 __SCPreferencesAddSessionKeys(prefs
);
1176 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1177 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyCommit
);
1178 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyApply
);
1179 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, keys
, NULL
);
1182 if (runLoop
!= NULL
) {
1183 prefsPrivate
->rls
= SCDynamicStoreCreateRunLoopSource(NULL
, prefsPrivate
->session
, 0);
1184 prefsPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1187 SC_log(LOG_DEBUG
, "scheduled");
1189 prefsPrivate
->scheduled
= TRUE
;
1192 if (queue
!= NULL
) {
1193 ok
= SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, queue
);
1195 prefsPrivate
->scheduled
= FALSE
;
1196 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
1197 __SCPreferencesRemoveSession(prefs
);
1201 prefsPrivate
->dispatchQueue
= queue
;
1202 dispatch_retain(prefsPrivate
->dispatchQueue
);
1204 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
1206 * if we do not already have notifications scheduled with
1207 * this runLoop / runLoopMode
1209 CFRunLoopAddSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
1212 _SC_schedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
);
1219 pthread_mutex_unlock(&prefsPrivate
->lock
);
1225 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
1226 CFRunLoopRef runLoop
,
1227 CFStringRef runLoopMode
)
1229 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1233 pthread_mutex_lock(&prefsPrivate
->lock
);
1235 if ((runLoop
!= NULL
) && !prefsPrivate
->scheduled
) { // if we should be scheduled (but are not)
1236 _SCErrorSet(kSCStatusInvalidArgument
);
1240 if (((runLoop
== NULL
) && (prefsPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
1241 ((runLoop
!= NULL
) && (prefsPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1242 _SCErrorSet(kSCStatusInvalidArgument
);
1246 if (runLoop
== NULL
) {
1247 SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, NULL
);
1248 dispatch_release(prefsPrivate
->dispatchQueue
);
1249 prefsPrivate
->dispatchQueue
= NULL
;
1251 if (!_SC_unschedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
, FALSE
)) {
1252 // if not currently scheduled on this runLoop / runLoopMode
1253 _SCErrorSet(kSCStatusInvalidArgument
);
1257 n
= CFArrayGetCount(prefsPrivate
->rlList
);
1258 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
1260 * if we are no longer scheduled to receive notifications for
1261 * this runLoop / runLoopMode
1263 CFRunLoopRemoveSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
1266 // if *all* notifications have been unscheduled
1267 CFRelease(prefsPrivate
->rlList
);
1268 prefsPrivate
->rlList
= NULL
;
1269 CFRunLoopSourceInvalidate(prefsPrivate
->rls
);
1270 CFRelease(prefsPrivate
->rls
);
1271 prefsPrivate
->rls
= NULL
;
1277 CFArrayRef changedKeys
;
1279 SC_log(LOG_DEBUG
, "unscheduled");
1281 // if *all* notifications have been unscheduled
1282 prefsPrivate
->scheduled
= FALSE
;
1284 // no need to track changes
1285 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
1287 // clear out any pending notifications
1288 changedKeys
= SCDynamicStoreCopyNotifiedKeys(prefsPrivate
->session
);
1289 if (changedKeys
!= NULL
) {
1290 CFRelease(changedKeys
);
1293 // remove SCDynamicStore session, release 'prefs' reference
1294 __SCPreferencesRemoveSession(prefs
);
1301 pthread_mutex_unlock(&prefsPrivate
->lock
);
1307 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
1308 CFRunLoopRef runLoop
,
1309 CFStringRef runLoopMode
)
1311 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1312 _SCErrorSet(kSCStatusInvalidArgument
);
1316 return __SCPreferencesScheduleWithRunLoop(prefs
, runLoop
, runLoopMode
, NULL
);
1321 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
1322 CFRunLoopRef runLoop
,
1323 CFStringRef runLoopMode
)
1325 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1326 _SCErrorSet(kSCStatusInvalidArgument
);
1330 return __SCPreferencesUnscheduleFromRunLoop(prefs
, runLoop
, runLoopMode
);
1335 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs
,
1336 dispatch_queue_t queue
)
1340 if (!isA_SCPreferences(prefs
)) {
1341 /* sorry, you must provide a session */
1342 _SCErrorSet(kSCStatusNoPrefsSession
);
1346 if (queue
!= NULL
) {
1347 ok
= __SCPreferencesScheduleWithRunLoop(prefs
, NULL
, NULL
, queue
);
1349 ok
= __SCPreferencesUnscheduleFromRunLoop(prefs
, NULL
, NULL
);
1357 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs
)
1360 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1361 uint32_t status
= kSCStatusOK
;
1363 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
1368 // have the helper "synchronize" the prefs
1369 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
1370 SCHELPER_MSG_PREFS_SYNCHRONIZE
,
1376 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
1377 _SCHelperClose(&prefsPrivate
->helper_port
);
1386 SCPreferencesSynchronize(SCPreferencesRef prefs
)
1388 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1390 if (!isA_SCPreferences(prefs
)) {
1391 /* sorry, you must provide a session */
1392 _SCErrorSet(kSCStatusNoPrefsSession
);
1396 SC_log(LOG_DEBUG
, "SCPreferences() synchronize: %s",
1397 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
1399 if (prefsPrivate
->authorizationData
!= NULL
) {
1400 __SCPreferencesSynchronize_helper(prefs
);
1402 if (prefsPrivate
->prefs
!= NULL
) {
1403 CFRelease(prefsPrivate
->prefs
);
1404 prefsPrivate
->prefs
= NULL
;
1406 if (prefsPrivate
->signature
!= NULL
) {
1407 CFRelease(prefsPrivate
->signature
);
1408 prefsPrivate
->signature
= NULL
;
1410 prefsPrivate
->accessed
= FALSE
;
1411 prefsPrivate
->changed
= FALSE
;