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
;