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
= FALSE
;
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 *opt_none
= "";
716 const char *opt_1
= opt_none
;
717 const char *opt_2
= opt_none
;
719 if (options
!= NULL
) {
723 if (authorization
!= NULL
) {
724 if (authorization
== kSCPreferencesUseEntitlementAuthorization
) {
725 opt_1
= "entitlement";
727 opt_1
= "authorization";
731 SC_log(LOG_DEBUG
, "create w/%s%s%s %@",
733 ((opt_2
!= opt_none
) && (opt_1
!= opt_none
)) ? " + " : "",
738 if (authorizationData
!= NULL
) CFRelease(authorizationData
);
740 return (SCPreferencesRef
)prefsPrivate
;
745 SCPreferencesCreateCompanion(SCPreferencesRef prefs
, CFStringRef companionPrefsID
)
747 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
748 SCPreferencesPrivateRef companionPrefs
= NULL
;
749 CFMutableStringRef newPrefsID
;
750 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
752 if (companionPrefsID
== NULL
) {
753 companionPrefsID
= PREFS_DEFAULT_CONFIG
;
755 if (CFStringFindWithOptions(companionPrefsID
,
757 CFRangeMake(0, CFStringGetLength(companionPrefsID
)),
760 // if companion prefsID contains a "/"
761 _SCErrorSet(kSCStatusInvalidArgument
);
766 if (prefsPrivate
->prefsID
== NULL
) {
767 if (CFEqual(companionPrefsID
, PREFS_DEFAULT_CONFIG
)) {
768 // if prefsID and companionPrefsID match
769 _SCErrorSet(kSCStatusInvalidArgument
);
772 newPrefsID
= CFStringCreateMutableCopy(allocator
, 0, companionPrefsID
);
774 CFIndex prefsIDLen
= CFStringGetLength(prefsPrivate
->prefsID
);
777 if (CFStringFindWithOptions(prefsPrivate
->prefsID
,
779 CFRangeMake(0, prefsIDLen
),
785 // if slash, check suffix
787 if (range
.location
>= prefsIDLen
) {
789 _SCErrorSet(kSCStatusInvalidArgument
);
792 range
.length
= prefsIDLen
- range
.location
;
793 suffix
= CFStringCreateWithSubstring(allocator
, prefsPrivate
->prefsID
, range
);
794 match
= CFEqual(suffix
, companionPrefsID
);
797 // if prefsID [suffix] and companionPrefsID match
798 _SCErrorSet(kSCStatusInvalidArgument
);
802 // replace the suffix
803 newPrefsID
= CFStringCreateMutableCopy(NULL
, 0, prefsPrivate
->prefsID
);
804 CFStringReplace(newPrefsID
, range
, companionPrefsID
);
805 } else if (!CFEqual(prefsPrivate
->prefsID
, companionPrefsID
)) {
806 // if no slash, prefsID and companionPrefsID differ
807 newPrefsID
= CFStringCreateMutableCopy(NULL
, 0, companionPrefsID
);
809 // if no slash, prefsID and companionPrefsID match
810 _SCErrorSet(kSCStatusInvalidArgument
);
814 assert(newPrefsID
!= NULL
);
816 pthread_mutex_lock(&prefsPrivate
->lock
);
817 if ((prefsPrivate
->companions
!= NULL
) &&
818 CFDictionaryGetValueIfPresent(prefsPrivate
->companions
,
820 (const void **)&companionPrefs
) &&
821 (companionPrefs
!= NULL
)) {
822 // if we already have a companion
823 SC_log(LOG_DEBUG
, "create [companion] reference %@", companionPrefs
);
824 CFRetain(companionPrefs
);
826 companionPrefs
= __SCPreferencesCreate(allocator
,
829 prefsPrivate
->authorizationData
,
830 prefsPrivate
->options
);
831 if (companionPrefs
!= NULL
) {
832 SCPreferencesPrivateRef companionPrefsPrivate
= (SCPreferencesPrivateRef
)companionPrefs
;
834 SC_log(LOG_DEBUG
, "create [companion] %@", companionPrefs
);
836 // add [strong] reference from companion to parent
837 companionPrefsPrivate
->parent
= CFRetain(prefs
);
839 // add [weak] reference from parent to this companion
840 if (prefsPrivate
->companions
== NULL
) {
841 prefsPrivate
->companions
= CFDictionaryCreateMutable(NULL
,
843 &kCFTypeDictionaryKeyCallBacks
,
846 CFDictionarySetValue(prefsPrivate
->companions
, newPrefsID
, companionPrefs
);
849 pthread_mutex_unlock(&prefsPrivate
->lock
);
851 CFRelease(newPrefsID
);
853 return (SCPreferencesRef
)companionPrefs
;
858 SCPreferencesGetTypeID(void) {
859 pthread_once(&initialized
, __SCPreferencesInitialize
); /* initialize runtime */
860 return __kSCPreferencesTypeID
;
865 prefsNotify(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
867 #pragma unused(store)
869 void (*context_release
)(const void *);
872 SCPreferencesNotification notify
= 0;
873 SCPreferencesRef prefs
= (SCPreferencesRef
)info
;
874 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
875 SCPreferencesCallBack rlsFunction
;
877 n
= (changedKeys
!= NULL
) ? CFArrayGetCount(changedKeys
) : 0;
878 for (i
= 0; i
< n
; i
++) {
881 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
884 if (CFEqual(key
, prefsPrivate
->sessionKeyCommit
)) {
885 // if preferences have been saved
886 notify
|= kSCPreferencesNotificationCommit
;
891 if (CFEqual(key
, prefsPrivate
->sessionKeyApply
)) {
892 // if stored preferences should be applied to current configuration
893 notify
|= kSCPreferencesNotificationApply
;
903 pthread_mutex_lock(&prefsPrivate
->lock
);
906 rlsFunction
= prefsPrivate
->rlsFunction
;
907 if (prefsPrivate
->rlsContext
.retain
!= NULL
) {
908 context_info
= (void *)prefsPrivate
->rlsContext
.retain(prefsPrivate
->rlsContext
.info
);
909 context_release
= prefsPrivate
->rlsContext
.release
;
911 context_info
= prefsPrivate
->rlsContext
.info
;
912 context_release
= NULL
;
915 pthread_mutex_unlock(&prefsPrivate
->lock
);
917 if (rlsFunction
!= NULL
) {
918 SC_log(LOG_DEBUG
, "exec SCPreferences callout: %s%s%s",
919 ((notify
& kSCPreferencesNotificationCommit
) != 0) ? "commit" : "",
920 (((notify
& kSCPreferencesNotificationCommit
) != 0) &&
921 ((notify
& kSCPreferencesNotificationApply
) != 0)) ? ", " : "",
922 ((notify
& kSCPreferencesNotificationApply
) != 0) ? "apply" : "");
923 (*rlsFunction
)(prefs
, notify
, context_info
);
926 if (context_release
!= NULL
) {
927 (*context_release
)(context_info
);
934 __private_extern__
void
935 __SCPreferencesAddSessionKeys(SCPreferencesRef prefs
)
937 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
939 /* create the session "commit" key */
940 if (prefsPrivate
->sessionKeyCommit
== NULL
) {
941 prefsPrivate
->sessionKeyCommit
= _SCPNotificationKey(NULL
,
942 prefsPrivate
->prefsID
,
943 kSCPreferencesKeyCommit
);
946 /* create the session "apply" key */
947 if (prefsPrivate
->sessionKeyApply
== NULL
) {
948 prefsPrivate
->sessionKeyApply
= _SCPNotificationKey(NULL
,
949 prefsPrivate
->prefsID
,
950 kSCPreferencesKeyApply
);
957 __private_extern__ Boolean
958 __SCPreferencesAddSession(SCPreferencesRef prefs
)
960 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
961 SCDynamicStoreContext context
= { 0
967 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
969 if (prefsPrivate
->sessionRefcnt
== 0) {
970 /* establish a dynamic store session */
971 prefsPrivate
->session
= SCDynamicStoreCreate(allocator
,
975 if (prefsPrivate
->session
== NULL
) {
976 SC_log(LOG_ERR
, "could not add SCDynamicStore session (for prefs): %s",
977 SCErrorString(SCError()));
981 SC_log(LOG_DEBUG
, "added SCDynamicStore session (for prefs)");
984 prefsPrivate
->sessionRefcnt
++;
989 __private_extern__
void
990 __SCPreferencesRemoveSession(SCPreferencesRef prefs
)
992 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
994 if (prefsPrivate
->sessionRefcnt
> 0) {
995 if (--prefsPrivate
->sessionRefcnt
== 0) {
996 CFRelease(prefsPrivate
->session
);
997 prefsPrivate
->session
= NULL
;
999 SC_log(LOG_DEBUG
, "removed SCDynamicStore session (for prefs)");
1008 appendLockedPreferences(const void *key
, const void *value
, void *context
)
1011 CFMutableStringRef str
= (CFMutableStringRef
)context
;
1013 CFStringAppendFormat(str
, NULL
, CFSTR("%s%@"),
1014 (CFStringGetLength(str
) > 0) ? "\n" : "",
1020 __private_extern__
void
1021 __SCPreferencesUpdateLockedState(SCPreferencesRef prefs
, Boolean locked
)
1023 static dispatch_queue_t lockedQueue
;
1024 static CFMutableDictionaryRef lockedState
;
1025 static dispatch_once_t once
;
1026 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1028 dispatch_once(&once
, ^{
1029 os_state_block_t state_block
;
1031 lockedQueue
= dispatch_queue_create("SCPreferences locked state queue", NULL
);
1033 lockedState
= CFDictionaryCreateMutable(NULL
,
1035 NULL
, // NO retain/release
1036 &kCFTypeDictionaryValueCallBacks
);
1038 state_block
= ^os_state_data_t(os_state_hints_t hints
) {
1039 #pragma unused(hints)
1040 CFDataRef data
= NULL
;
1042 os_state_data_t state_data
;
1043 size_t state_data_size
;
1045 CFMutableStringRef str
;
1047 if (CFDictionaryGetCount(lockedState
) == 0) {
1048 // if no locked preferences
1052 str
= CFStringCreateMutable(NULL
, 0);
1053 CFDictionaryApplyFunction(lockedState
, appendLockedPreferences
, str
);
1054 ok
= _SCSerialize(str
, &data
, NULL
, NULL
);
1057 state_len
= (ok
&& (data
!= NULL
)) ? CFDataGetLength(data
) : 0;
1058 state_data_size
= OS_STATE_DATA_SIZE_NEEDED(state_len
);
1059 if (state_data_size
> MAX_STATEDUMP_SIZE
) {
1060 SC_log(LOG_ERR
, "locked SCPreferences : state data too large (%zd > %zd)",
1062 (size_t)MAX_STATEDUMP_SIZE
);
1063 if (data
!= NULL
) CFRelease(data
);
1067 state_data
= calloc(1, state_data_size
);
1068 if (state_data
== NULL
) {
1069 SC_log(LOG_ERR
, "locked SCPreferences: could not allocate state data");
1070 if (data
!= NULL
) CFRelease(data
);
1074 state_data
->osd_type
= OS_STATE_DATA_SERIALIZED_NSCF_OBJECT
;
1075 state_data
->osd_data_size
= (uint32_t)state_len
;
1076 strlcpy(state_data
->osd_title
, "open/locked SCPreferences", sizeof(state_data
->osd_title
));
1077 if (state_len
> 0) {
1078 memcpy(state_data
->osd_data
, CFDataGetBytePtr(data
), state_len
);
1080 if (data
!= NULL
) CFRelease(data
);
1085 (void) os_state_add_handler(lockedQueue
, state_block
);
1088 // update the locked state
1089 prefsPrivate
->locked
= locked
;
1091 // add (or update) the locked preferences
1092 dispatch_sync(lockedQueue
, ^{
1096 str
= CFCopyDescription(prefs
);
1097 CFDictionarySetValue(lockedState
, prefs
, str
);
1100 CFDictionaryRemoveValue(lockedState
, prefs
);
1109 SCPreferencesSetCallback(SCPreferencesRef prefs
,
1110 SCPreferencesCallBack callout
,
1111 SCPreferencesContext
*context
)
1113 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1115 if (!isA_SCPreferences(prefs
)) {
1116 /* sorry, you must provide a session */
1117 _SCErrorSet(kSCStatusNoPrefsSession
);
1121 pthread_mutex_lock(&prefsPrivate
->lock
);
1123 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
1124 /* let go of the current context */
1125 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
1128 prefsPrivate
->rlsFunction
= callout
;
1129 prefsPrivate
->rlsContext
.info
= NULL
;
1130 prefsPrivate
->rlsContext
.retain
= NULL
;
1131 prefsPrivate
->rlsContext
.release
= NULL
;
1132 prefsPrivate
->rlsContext
.copyDescription
= NULL
;
1133 if (context
!= NULL
) {
1134 memcpy(&prefsPrivate
->rlsContext
, context
, sizeof(SCPreferencesContext
));
1135 if (context
->retain
!= NULL
) {
1136 prefsPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
1140 pthread_mutex_unlock(&prefsPrivate
->lock
);
1147 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
1148 CFRunLoopRef runLoop
,
1149 CFStringRef runLoopMode
,
1150 dispatch_queue_t queue
)
1153 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1155 pthread_mutex_lock(&prefsPrivate
->lock
);
1157 if ((prefsPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
1158 ((queue
!= NULL
) && prefsPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
1159 _SCErrorSet(kSCStatusInvalidArgument
);
1163 if (!prefsPrivate
->scheduled
) {
1164 CFMutableArrayRef keys
;
1166 // add SCDynamicStore session (for notifications) ... and hold a 'prefs' reference
1167 if (prefsPrivate
->session
== NULL
) {
1168 ok
= __SCPreferencesAddSession(prefs
);
1172 assert(prefsPrivate
->session
!= NULL
);
1175 // add SCDynamicStore "keys"
1176 __SCPreferencesAddSessionKeys(prefs
);
1178 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1179 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyCommit
);
1180 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyApply
);
1181 ok
= SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, keys
, NULL
);
1184 SC_log(LOG_ERR
, "could not set SCDynamicStore notification keys (for prefs): %s",
1185 SCErrorString(SCError()));
1189 if (runLoop
!= NULL
) {
1190 prefsPrivate
->rls
= SCDynamicStoreCreateRunLoopSource(NULL
, prefsPrivate
->session
, 0);
1191 if (prefsPrivate
->rls
== NULL
) {
1192 SC_log(LOG_ERR
, "could not create SCDynamicStore runloop source (for prefs): %s",
1193 SCErrorString(SCError()));
1197 prefsPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1200 SC_log(LOG_DEBUG
, "scheduled");
1202 prefsPrivate
->scheduled
= TRUE
;
1205 if (queue
!= NULL
) {
1206 ok
= SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, queue
);
1208 prefsPrivate
->scheduled
= FALSE
;
1209 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
1210 __SCPreferencesRemoveSession(prefs
);
1214 prefsPrivate
->dispatchQueue
= queue
;
1215 dispatch_retain(prefsPrivate
->dispatchQueue
);
1217 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
1219 * if we do not already have notifications scheduled with
1220 * this runLoop / runLoopMode
1222 CFRunLoopAddSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
1225 _SC_schedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
);
1232 pthread_mutex_unlock(&prefsPrivate
->lock
);
1238 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
1239 CFRunLoopRef runLoop
,
1240 CFStringRef runLoopMode
)
1242 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1246 pthread_mutex_lock(&prefsPrivate
->lock
);
1248 if ((runLoop
!= NULL
) && !prefsPrivate
->scheduled
) { // if we should be scheduled (but are not)
1249 _SCErrorSet(kSCStatusInvalidArgument
);
1253 if (((runLoop
== NULL
) && (prefsPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
1254 ((runLoop
!= NULL
) && (prefsPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1255 _SCErrorSet(kSCStatusInvalidArgument
);
1259 if (runLoop
== NULL
) {
1260 SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, NULL
);
1261 dispatch_release(prefsPrivate
->dispatchQueue
);
1262 prefsPrivate
->dispatchQueue
= NULL
;
1264 if (!_SC_unschedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
, FALSE
)) {
1265 // if not currently scheduled on this runLoop / runLoopMode
1266 _SCErrorSet(kSCStatusInvalidArgument
);
1270 n
= CFArrayGetCount(prefsPrivate
->rlList
);
1271 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
1273 * if we are no longer scheduled to receive notifications for
1274 * this runLoop / runLoopMode
1276 CFRunLoopRemoveSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
1279 // if *all* notifications have been unscheduled
1280 CFRelease(prefsPrivate
->rlList
);
1281 prefsPrivate
->rlList
= NULL
;
1282 CFRunLoopSourceInvalidate(prefsPrivate
->rls
);
1283 CFRelease(prefsPrivate
->rls
);
1284 prefsPrivate
->rls
= NULL
;
1290 CFArrayRef changedKeys
;
1292 SC_log(LOG_DEBUG
, "unscheduled");
1294 // if *all* notifications have been unscheduled
1295 prefsPrivate
->scheduled
= FALSE
;
1297 // no need to track changes
1298 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
1300 // clear out any pending notifications
1301 changedKeys
= SCDynamicStoreCopyNotifiedKeys(prefsPrivate
->session
);
1302 if (changedKeys
!= NULL
) {
1303 CFRelease(changedKeys
);
1306 // remove SCDynamicStore session, release 'prefs' reference
1307 __SCPreferencesRemoveSession(prefs
);
1314 pthread_mutex_unlock(&prefsPrivate
->lock
);
1320 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
1321 CFRunLoopRef runLoop
,
1322 CFStringRef runLoopMode
)
1324 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1325 _SCErrorSet(kSCStatusInvalidArgument
);
1329 return __SCPreferencesScheduleWithRunLoop(prefs
, runLoop
, runLoopMode
, NULL
);
1334 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
1335 CFRunLoopRef runLoop
,
1336 CFStringRef runLoopMode
)
1338 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1339 _SCErrorSet(kSCStatusInvalidArgument
);
1343 return __SCPreferencesUnscheduleFromRunLoop(prefs
, runLoop
, runLoopMode
);
1348 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs
,
1349 dispatch_queue_t queue
)
1353 if (!isA_SCPreferences(prefs
)) {
1354 /* sorry, you must provide a session */
1355 _SCErrorSet(kSCStatusNoPrefsSession
);
1359 if (queue
!= NULL
) {
1360 ok
= __SCPreferencesScheduleWithRunLoop(prefs
, NULL
, NULL
, queue
);
1362 ok
= __SCPreferencesUnscheduleFromRunLoop(prefs
, NULL
, NULL
);
1370 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs
)
1373 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1374 uint32_t status
= kSCStatusOK
;
1376 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
1381 // have the helper "synchronize" the prefs
1382 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
1383 SCHELPER_MSG_PREFS_SYNCHRONIZE
,
1389 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
1390 _SCHelperClose(&prefsPrivate
->helper_port
);
1399 SCPreferencesSynchronize(SCPreferencesRef prefs
)
1401 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1403 if (!isA_SCPreferences(prefs
)) {
1404 /* sorry, you must provide a session */
1405 _SCErrorSet(kSCStatusNoPrefsSession
);
1409 SC_log(LOG_DEBUG
, "SCPreferences() synchronize: %s",
1410 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
1412 if (prefsPrivate
->authorizationData
!= NULL
) {
1413 __SCPreferencesSynchronize_helper(prefs
);
1415 if (prefsPrivate
->prefs
!= NULL
) {
1416 CFRelease(prefsPrivate
->prefs
);
1417 prefsPrivate
->prefs
= NULL
;
1419 if (prefsPrivate
->signature
!= NULL
) {
1420 CFRelease(prefsPrivate
->signature
);
1421 prefsPrivate
->signature
= NULL
;
1423 prefsPrivate
->accessed
= FALSE
;
1424 prefsPrivate
->changed
= FALSE
;