2 * Copyright(c) 2000-2018 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 <os/availability.h>
38 #include <TargetConditionals.h>
43 #include <sys/errno.h>
44 #include <sys/cdefs.h>
45 #include <dispatch/dispatch.h>
47 #include "SCPreferencesInternal.h"
49 #include "SCHelper_client.h"
50 #include "dy_framework.h"
53 const AuthorizationRef kSCPreferencesUseEntitlementAuthorization
= (AuthorizationRef
)CFSTR("UseEntitlement");
56 __private_extern__ os_log_t
57 __log_SCPreferences(void)
59 static os_log_t log
= NULL
;
62 log
= os_log_create("com.apple.SystemConfiguration", "SCPreferences");
69 static __inline__ CFTypeRef
70 isA_SCPreferences(CFTypeRef obj
)
72 return (isA_CFType(obj
, SCPreferencesGetTypeID()));
77 __SCPreferencesCopyDescription(CFTypeRef cf
) {
78 CFAllocatorRef allocator
= CFGetAllocator(cf
);
79 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)cf
;
80 CFMutableStringRef result
;
82 result
= CFStringCreateMutable(allocator
, 0);
83 CFStringAppendFormat(result
, NULL
, CFSTR("<SCPreferences %p [%p]> {"), cf
, allocator
);
84 CFStringAppendFormat(result
, NULL
, CFSTR("name = %@"), prefsPrivate
->name
);
85 CFStringAppendFormat(result
, NULL
, CFSTR(", id = %@"),
86 prefsPrivate
->prefsID
!= NULL
? prefsPrivate
->prefsID
: CFSTR("[default]"));
87 CFStringAppendFormat(result
, NULL
, CFSTR(", path = %s"),
88 prefsPrivate
->newPath
!= NULL
? prefsPrivate
->newPath
: prefsPrivate
->path
);
89 if (prefsPrivate
->accessed
) {
90 CFStringAppendFormat(result
, NULL
, CFSTR(", accessed"));
92 if (prefsPrivate
->changed
) {
93 CFStringAppendFormat(result
, NULL
, CFSTR(", changed"));
95 if (prefsPrivate
->locked
) {
96 CFStringAppendFormat(result
, NULL
, CFSTR(", locked"));
98 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
99 CFStringAppendFormat(result
, NULL
, CFSTR(", helper port = 0x%x"), prefsPrivate
->helper_port
);
101 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
108 __SCPreferencesDeallocate(CFTypeRef cf
)
110 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)cf
;
112 SC_log(LOG_DEBUG
, "release %@", prefsPrivate
);
114 /* release resources */
116 pthread_mutex_destroy(&prefsPrivate
->lock
);
118 if (prefsPrivate
->name
) CFRelease(prefsPrivate
->name
);
119 if (prefsPrivate
->prefsID
) CFRelease(prefsPrivate
->prefsID
);
120 if (prefsPrivate
->options
) CFRelease(prefsPrivate
->options
);
121 if (prefsPrivate
->path
) CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
122 if (prefsPrivate
->newPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->newPath
);
123 if (prefsPrivate
->lockFD
!= -1) {
124 if (prefsPrivate
->lockPath
!= NULL
) {
125 unlink(prefsPrivate
->lockPath
);
127 close(prefsPrivate
->lockFD
);
129 if (prefsPrivate
->lockPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->lockPath
);
130 if (prefsPrivate
->signature
) CFRelease(prefsPrivate
->signature
);
131 if (prefsPrivate
->sessionNoO_EXLOCK
!= NULL
) {
132 CFRelease(prefsPrivate
->sessionNoO_EXLOCK
);
134 if (prefsPrivate
->sessionKeyLock
) CFRelease(prefsPrivate
->sessionKeyLock
);
135 if (prefsPrivate
->sessionKeyCommit
) CFRelease(prefsPrivate
->sessionKeyCommit
);
136 if (prefsPrivate
->sessionKeyApply
) CFRelease(prefsPrivate
->sessionKeyApply
);
137 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
138 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
140 if (prefsPrivate
->prefs
) CFRelease(prefsPrivate
->prefs
);
141 if (prefsPrivate
->authorizationData
!= NULL
) CFRelease(prefsPrivate
->authorizationData
);
142 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
143 (void) _SCHelperExec(prefsPrivate
->helper_port
,
144 SCHELPER_MSG_PREFS_CLOSE
,
148 _SCHelperClose(&prefsPrivate
->helper_port
);
155 static CFTypeID __kSCPreferencesTypeID
= _kCFRuntimeNotATypeID
;
158 static const CFRuntimeClass __SCPreferencesClass
= {
160 "SCPreferences", // className
163 __SCPreferencesDeallocate
, // dealloc
166 NULL
, // copyFormattingDesc
167 __SCPreferencesCopyDescription
// copyDebugDesc
171 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
174 __SCPreferencesInitialize(void) {
175 /* register with CoreFoundation */
176 __kSCPreferencesTypeID
= _CFRuntimeRegisterClass(&__SCPreferencesClass
);
181 static SCPreferencesPrivateRef
182 __SCPreferencesCreatePrivate(CFAllocatorRef allocator
)
184 SCPreferencesPrivateRef prefsPrivate
;
187 /* initialize runtime */
188 pthread_once(&initialized
, __SCPreferencesInitialize
);
190 /* allocate prefs session */
191 size
= sizeof(SCPreferencesPrivate
) - sizeof(CFRuntimeBase
);
192 prefsPrivate
= (SCPreferencesPrivateRef
)_CFRuntimeCreateInstance(allocator
,
193 __kSCPreferencesTypeID
,
196 if (prefsPrivate
== NULL
) {
200 /* initialize non-zero/NULL members */
201 pthread_mutex_init(&prefsPrivate
->lock
, NULL
);
202 prefsPrivate
->lockFD
= -1;
203 prefsPrivate
->isRoot
= (geteuid() == 0);
209 __private_extern__ Boolean
210 __SCPreferencesCreate_helper(SCPreferencesRef prefs
)
212 CFDataRef data
= NULL
;
213 CFMutableDictionaryRef info
;
216 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
217 uint32_t status
= kSCStatusOK
;
219 uint32_t pid
= getpid();
222 ok
= _SCHelperOpen(prefsPrivate
->authorizationData
,
223 &prefsPrivate
->helper_port
);
228 // create a dictionary of information to pass to the helper
229 info
= CFDictionaryCreateMutable(NULL
,
231 &kCFTypeDictionaryKeyCallBacks
,
232 &kCFTypeDictionaryValueCallBacks
);
235 if (prefsPrivate
->prefsID
!= NULL
) {
236 CFDictionarySetValue(info
, CFSTR("prefsID"), prefsPrivate
->prefsID
);
240 if (prefsPrivate
->options
!= NULL
) {
241 CFDictionarySetValue(info
, CFSTR("options"), prefsPrivate
->options
);
244 // save preferences session "name"
245 CFDictionarySetValue(info
, CFSTR("name"), prefsPrivate
->name
);
248 num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &pid
);
249 CFDictionarySetValue(info
, CFSTR("PID"), num
);
253 str
= CFStringCreateWithCString(NULL
, getprogname(), kCFStringEncodingUTF8
);
254 CFDictionarySetValue(info
, CFSTR("PROC_NAME"), str
);
257 // serialize the info
258 ok
= _SCSerialize(info
, &data
, NULL
, NULL
);
260 if (data
== NULL
|| !ok
) {
264 // have the helper "open" the prefs
265 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
266 SCHELPER_MSG_PREFS_OPEN
,
270 if (data
!= NULL
) CFRelease(data
);
275 if (status
!= kSCStatusOK
) {
284 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
285 _SCHelperClose(&prefsPrivate
->helper_port
);
288 status
= kSCStatusAccessError
;
299 __SCPreferencesAccess_helper(SCPreferencesRef prefs
)
302 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
303 CFDictionaryRef serverDict
= NULL
;
304 CFDictionaryRef serverPrefs
= NULL
;
305 CFDictionaryRef serverSignature
= NULL
;
306 uint32_t status
= kSCStatusOK
;
307 CFDataRef reply
= NULL
;
309 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
310 ok
= __SCPreferencesCreate_helper(prefs
);
316 // have the helper "access" the prefs
317 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
318 SCHELPER_MSG_PREFS_ACCESS
,
326 if (status
!= kSCStatusOK
) {
334 ok
= _SCUnserialize((CFPropertyListRef
*)&serverDict
, reply
, NULL
, 0);
340 if (isA_CFDictionary(serverDict
)) {
341 serverPrefs
= CFDictionaryGetValue(serverDict
, CFSTR("preferences"));
342 serverPrefs
= isA_CFDictionary(serverPrefs
);
344 serverSignature
= CFDictionaryGetValue(serverDict
, CFSTR("signature"));
345 serverSignature
= isA_CFData(serverSignature
);
348 if ((serverPrefs
== NULL
) || (serverSignature
== NULL
)) {
349 if (serverDict
!= NULL
) CFRelease(serverDict
);
353 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, serverPrefs
);
354 prefsPrivate
->signature
= CFRetain(serverSignature
);
355 prefsPrivate
->accessed
= TRUE
;
356 CFRelease(serverDict
);
363 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
364 _SCHelperClose(&prefsPrivate
->helper_port
);
367 status
= kSCStatusAccessError
;
377 static SCPreferencesPrivateRef
378 __SCPreferencesCreate(CFAllocatorRef allocator
,
381 CFDataRef authorizationData
,
382 CFDictionaryRef options
)
384 SCPreferencesPrivateRef prefsPrivate
;
385 int sc_status
= kSCStatusOK
;
388 * allocate and initialize a new prefs session
390 prefsPrivate
= __SCPreferencesCreatePrivate(allocator
);
391 if (prefsPrivate
== NULL
) {
395 prefsPrivate
->name
= CFStringCreateCopy(allocator
, name
);
396 if (prefsID
!= NULL
) {
397 prefsPrivate
->prefsID
= CFStringCreateCopy(allocator
, prefsID
);
399 if (authorizationData
!= NULL
) {
400 prefsPrivate
->authorizationData
= CFRetain(authorizationData
);
402 if (options
!= NULL
) {
403 prefsPrivate
->options
= CFDictionaryCreateCopy(allocator
, options
);
409 * convert prefsID to path
411 prefsPrivate
->path
= __SCPreferencesPath(allocator
,
413 (prefsPrivate
->newPath
== NULL
));
414 if (prefsPrivate
->path
== NULL
) {
415 sc_status
= kSCStatusFailed
;
419 if (access(prefsPrivate
->path
, R_OK
) == 0) {
426 if ((prefsID
== NULL
) || !CFStringHasPrefix(prefsID
, CFSTR("/"))) {
427 /* if default preference ID or relative path */
428 if (prefsPrivate
->newPath
== NULL
) {
430 * we've looked in the "new" prefs directory
431 * without success. Save the "new" path and
432 * look in the "old" prefs directory.
434 prefsPrivate
->newPath
= prefsPrivate
->path
;
438 * we've looked in both the "new" and "old"
439 * prefs directories without success. Use
442 CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
443 prefsPrivate
->path
= prefsPrivate
->newPath
;
444 prefsPrivate
->newPath
= NULL
;
448 /* no preference data, start fresh */
449 sc_status
= kSCStatusNoConfigFile
;
453 if (prefsPrivate
->authorizationData
!= NULL
) {
454 /* no problem, we'll be using the helper */
458 SC_log(LOG_INFO
, "open() failed: %s", strerror(errno
));
459 sc_status
= kSCStatusAccessError
;
462 SC_log(LOG_INFO
, "open() failed: %s", strerror(errno
));
463 sc_status
= kSCStatusFailed
;
469 CFRelease(prefsPrivate
);
470 _SCErrorSet(sc_status
);
476 _SCErrorSet(sc_status
);
481 __private_extern__
void
482 __SCPreferencesAccess(SCPreferencesRef prefs
)
484 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
486 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
489 if (prefsPrivate
->accessed
) {
490 // if preference data has already been accessed
494 if (access(prefsPrivate
->path
, R_OK
) == 0) {
495 fd
= open(prefsPrivate
->path
, O_RDONLY
, 0644);
501 if (fstat(fd
, &statBuf
) == -1) {
502 SC_log(LOG_NOTICE
, "fstat() failed: %s", strerror(errno
));
503 bzero(&statBuf
, sizeof(statBuf
));
508 /* no preference data, start fresh */
512 if (prefsPrivate
->authorizationData
!= NULL
) {
513 if (__SCPreferencesAccess_helper(prefs
)) {
516 SC_log(LOG_NOTICE
, "__SCPreferencesAccess_helper() failed: %s",
517 SCErrorString(SCError()));
523 SC_log(LOG_NOTICE
, "open() failed: %s", strerror(errno
));
526 bzero(&statBuf
, sizeof(statBuf
));
529 if (prefsPrivate
->signature
!= NULL
) CFRelease(prefsPrivate
->signature
);
530 prefsPrivate
->signature
= __SCPSignatureFromStatbuf(&statBuf
);
532 if (statBuf
.st_size
> 0) {
533 CFDictionaryRef dict
;
534 CFErrorRef error
= NULL
;
535 CFMutableDataRef xmlData
;
538 * extract property list
540 xmlData
= CFDataCreateMutable(allocator
, (CFIndex
)statBuf
.st_size
);
541 CFDataSetLength(xmlData
, (CFIndex
)statBuf
.st_size
);
542 if (read(fd
, (void *)CFDataGetBytePtr(xmlData
), (CFIndex
)statBuf
.st_size
) != (CFIndex
)statBuf
.st_size
) {
543 /* corrupt prefs file, start fresh */
544 SC_log(LOG_INFO
, "read(): could not load preference data");
553 dict
= CFPropertyListCreateWithData(allocator
, xmlData
, kCFPropertyListImmutable
, NULL
, &error
);
556 /* corrupt prefs file, start fresh */
558 SC_log(LOG_NOTICE
, "CFPropertyListCreateWithData(): %@", error
);
565 * make sure that we've got a dictionary
567 if (!isA_CFDictionary(dict
)) {
568 /* corrupt prefs file, start fresh */
569 SC_log(LOG_INFO
, "CFGetTypeID(): not a dictionary");
574 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(allocator
, 0, dict
);
584 if (prefsPrivate
->prefs
== NULL
) {
586 * new file, create empty preferences
588 // SC_log(LOG_INFO, "creating new preferences file");
589 prefsPrivate
->prefs
= CFDictionaryCreateMutable(allocator
,
591 &kCFTypeDictionaryKeyCallBacks
,
592 &kCFTypeDictionaryValueCallBacks
);
593 prefsPrivate
->changed
= TRUE
;
596 SC_log(LOG_DEBUG
, "SCPreferences() access: %s",
597 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
599 prefsPrivate
->accessed
= TRUE
;
605 SCPreferencesCreate(CFAllocatorRef allocator
,
609 SCPreferencesPrivateRef prefsPrivate
;
611 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, NULL
, NULL
);
612 if (prefsPrivate
!= NULL
) {
613 SC_log(LOG_DEBUG
, "create %@", prefsPrivate
);
616 return (SCPreferencesRef
)prefsPrivate
;
621 SCPreferencesCreateWithAuthorization(CFAllocatorRef allocator
,
624 AuthorizationRef authorization
)
626 SCPreferencesRef prefs
;
628 #if !TARGET_OS_IPHONE
629 if (authorization
== NULL
) {
630 authorization
= kSCPreferencesUseEntitlementAuthorization
;
632 #else // !TARGET_OS_IPHONE
633 authorization
= kSCPreferencesUseEntitlementAuthorization
;
634 #endif // !TARGET_OS_IPHONE
636 prefs
= SCPreferencesCreateWithOptions(allocator
, name
, prefsID
, authorization
, NULL
);
642 SCPreferencesCreateWithOptions(CFAllocatorRef allocator
,
645 AuthorizationRef authorization
,
646 CFDictionaryRef options
)
648 CFDataRef authorizationData
= NULL
;
649 SCPreferencesPrivateRef prefsPrivate
;
651 if (options
!= NULL
) {
652 if (!isA_CFDictionary(options
)) {
653 _SCErrorSet(kSCStatusInvalidArgument
);
658 if (authorization
!= NULL
) {
659 CFMutableDictionaryRef authorizationDict
;
661 CFStringRef bundleID
= NULL
;
663 authorizationDict
= CFDictionaryCreateMutable(NULL
,
665 &kCFTypeDictionaryKeyCallBacks
,
666 &kCFTypeDictionaryValueCallBacks
);
667 #if !TARGET_OS_IPHONE
668 if (authorization
!= kSCPreferencesUseEntitlementAuthorization
) {
670 AuthorizationExternalForm extForm
;
673 os_status
= AuthorizationMakeExternalForm(authorization
, &extForm
);
674 if (os_status
!= errAuthorizationSuccess
) {
675 SC_log(LOG_INFO
, "AuthorizationMakeExternalForm() failed");
676 _SCErrorSet(kSCStatusInvalidArgument
);
677 CFRelease(authorizationDict
);
681 data
= CFDataCreate(NULL
, (const UInt8
*)extForm
.bytes
, sizeof(extForm
.bytes
));
682 CFDictionaryAddValue(authorizationDict
,
683 kSCHelperAuthAuthorization
,
687 #endif // !TARGET_OS_IPHONE
689 /* get the application/executable/bundle name */
690 bundle
= CFBundleGetMainBundle();
691 if (bundle
!= NULL
) {
692 bundleID
= CFBundleGetIdentifier(bundle
);
693 if (bundleID
!= NULL
) {
698 url
= CFBundleCopyExecutableURL(bundle
);
700 bundleID
= CFURLCopyPath(url
);
705 if (bundleID
!= NULL
) {
706 if (CFEqual(bundleID
, CFSTR("/"))) {
712 if (bundleID
== NULL
) {
713 bundleID
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Unknown(%d)"), getpid());
715 CFDictionaryAddValue(authorizationDict
,
716 kSCHelperAuthCallerInfo
,
720 if (authorizationDict
!= NULL
) {
721 (void) _SCSerialize((CFPropertyListRef
)authorizationDict
,
725 CFRelease(authorizationDict
);
729 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, authorizationData
, options
);
730 if (prefsPrivate
!= NULL
) {
731 const char *astr
= "";
732 const char *ostr
= "";
734 if (options
!= NULL
) {
738 if (authorization
!= NULL
) {
739 if (authorization
== kSCPreferencesUseEntitlementAuthorization
) {
740 astr
= "entitlement";
742 astr
= "authorization";
746 SC_log(LOG_DEBUG
, "create w/%s%s%s %@",
748 ((ostr
!= "") && (astr
!= "")) ? " + " : "",
753 if (authorizationData
!= NULL
) CFRelease(authorizationData
);
755 return (SCPreferencesRef
)prefsPrivate
;
760 SCPreferencesGetTypeID(void) {
761 pthread_once(&initialized
, __SCPreferencesInitialize
); /* initialize runtime */
762 return __kSCPreferencesTypeID
;
767 prefsNotify(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
769 #pragma unused(store)
771 void (*context_release
)(const void *);
774 SCPreferencesNotification notify
= 0;
775 SCPreferencesRef prefs
= (SCPreferencesRef
)info
;
776 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
777 SCPreferencesCallBack rlsFunction
;
779 n
= (changedKeys
!= NULL
) ? CFArrayGetCount(changedKeys
) : 0;
780 for (i
= 0; i
< n
; i
++) {
783 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
786 if (CFEqual(key
, prefsPrivate
->sessionKeyCommit
)) {
787 // if preferences have been saved
788 notify
|= kSCPreferencesNotificationCommit
;
793 if (CFEqual(key
, prefsPrivate
->sessionKeyApply
)) {
794 // if stored preferences should be applied to current configuration
795 notify
|= kSCPreferencesNotificationApply
;
805 pthread_mutex_lock(&prefsPrivate
->lock
);
808 rlsFunction
= prefsPrivate
->rlsFunction
;
809 if (prefsPrivate
->rlsContext
.retain
!= NULL
) {
810 context_info
= (void *)prefsPrivate
->rlsContext
.retain(prefsPrivate
->rlsContext
.info
);
811 context_release
= prefsPrivate
->rlsContext
.release
;
813 context_info
= prefsPrivate
->rlsContext
.info
;
814 context_release
= NULL
;
817 pthread_mutex_unlock(&prefsPrivate
->lock
);
819 if (rlsFunction
!= NULL
) {
820 SC_log(LOG_DEBUG
, "exec SCPreferences callout: %s%s%s",
821 ((notify
& kSCPreferencesNotificationCommit
) != 0) ? "commit" : "",
822 (((notify
& kSCPreferencesNotificationCommit
) != 0) &&
823 ((notify
& kSCPreferencesNotificationApply
) != 0)) ? ", " : "",
824 ((notify
& kSCPreferencesNotificationApply
) != 0) ? "apply" : "");
825 (*rlsFunction
)(prefs
, notify
, context_info
);
828 if (context_release
!= NULL
) {
829 (*context_release
)(context_info
);
836 __private_extern__
void
837 __SCPreferencesAddSessionKeys(SCPreferencesRef prefs
)
839 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
841 /* create the session "commit" key */
842 if (prefsPrivate
->sessionKeyCommit
== NULL
) {
843 prefsPrivate
->sessionKeyCommit
= _SCPNotificationKey(NULL
,
844 prefsPrivate
->prefsID
,
845 kSCPreferencesKeyCommit
);
848 /* create the session "apply" key */
849 if (prefsPrivate
->sessionKeyApply
== NULL
) {
850 prefsPrivate
->sessionKeyApply
= _SCPNotificationKey(NULL
,
851 prefsPrivate
->prefsID
,
852 kSCPreferencesKeyApply
);
859 __private_extern__ Boolean
860 __SCPreferencesAddSession(SCPreferencesRef prefs
)
862 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
863 SCDynamicStoreContext context
= { 0
869 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
871 if (prefsPrivate
->sessionRefcnt
== 0) {
872 /* establish a dynamic store session */
873 prefsPrivate
->session
= SCDynamicStoreCreate(allocator
,
877 if (prefsPrivate
->session
== NULL
) {
878 SC_log(LOG_INFO
, "SCDynamicStoreCreate() failed");
882 SC_log(LOG_DEBUG
, "added SCDynamicStore session (for prefs)");
885 prefsPrivate
->sessionRefcnt
++;
890 __private_extern__
void
891 __SCPreferencesRemoveSession(SCPreferencesRef prefs
)
893 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
895 if (prefsPrivate
->sessionRefcnt
> 0) {
896 if (--prefsPrivate
->sessionRefcnt
== 0) {
897 CFRelease(prefsPrivate
->session
);
898 prefsPrivate
->session
= NULL
;
900 SC_log(LOG_DEBUG
, "removed SCDynamicStore session (for prefs)");
909 SCPreferencesSetCallback(SCPreferencesRef prefs
,
910 SCPreferencesCallBack callout
,
911 SCPreferencesContext
*context
)
913 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
915 if (!isA_SCPreferences(prefs
)) {
916 /* sorry, you must provide a session */
917 _SCErrorSet(kSCStatusNoPrefsSession
);
921 pthread_mutex_lock(&prefsPrivate
->lock
);
923 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
924 /* let go of the current context */
925 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
928 prefsPrivate
->rlsFunction
= callout
;
929 prefsPrivate
->rlsContext
.info
= NULL
;
930 prefsPrivate
->rlsContext
.retain
= NULL
;
931 prefsPrivate
->rlsContext
.release
= NULL
;
932 prefsPrivate
->rlsContext
.copyDescription
= NULL
;
933 if (context
!= NULL
) {
934 bcopy(context
, &prefsPrivate
->rlsContext
, sizeof(SCPreferencesContext
));
935 if (context
->retain
!= NULL
) {
936 prefsPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
940 pthread_mutex_unlock(&prefsPrivate
->lock
);
947 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
948 CFRunLoopRef runLoop
,
949 CFStringRef runLoopMode
,
950 dispatch_queue_t queue
)
953 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
955 pthread_mutex_lock(&prefsPrivate
->lock
);
957 if ((prefsPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
958 ((queue
!= NULL
) && prefsPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
959 _SCErrorSet(kSCStatusInvalidArgument
);
963 if (!prefsPrivate
->scheduled
) {
964 CFMutableArrayRef keys
;
966 // add SCDynamicStore session (for notifications) ... and hold a 'prefs' reference
967 if (prefsPrivate
->session
== NULL
) {
968 ok
= __SCPreferencesAddSession(prefs
);
972 assert(prefsPrivate
->session
!= NULL
);
975 // add SCDynamicStore "keys"
976 __SCPreferencesAddSessionKeys(prefs
);
978 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
979 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyCommit
);
980 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyApply
);
981 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, keys
, NULL
);
984 if (runLoop
!= NULL
) {
985 prefsPrivate
->rls
= SCDynamicStoreCreateRunLoopSource(NULL
, prefsPrivate
->session
, 0);
986 prefsPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
989 SC_log(LOG_DEBUG
, "scheduled");
991 prefsPrivate
->scheduled
= TRUE
;
995 ok
= SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, queue
);
997 prefsPrivate
->scheduled
= FALSE
;
998 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
999 __SCPreferencesRemoveSession(prefs
);
1003 prefsPrivate
->dispatchQueue
= queue
;
1004 dispatch_retain(prefsPrivate
->dispatchQueue
);
1006 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
1008 * if we do not already have notifications scheduled with
1009 * this runLoop / runLoopMode
1011 CFRunLoopAddSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
1014 _SC_schedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
);
1021 pthread_mutex_unlock(&prefsPrivate
->lock
);
1027 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
1028 CFRunLoopRef runLoop
,
1029 CFStringRef runLoopMode
)
1031 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1035 pthread_mutex_lock(&prefsPrivate
->lock
);
1037 if ((runLoop
!= NULL
) && !prefsPrivate
->scheduled
) { // if we should be scheduled (but are not)
1038 _SCErrorSet(kSCStatusInvalidArgument
);
1042 if (((runLoop
== NULL
) && (prefsPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
1043 ((runLoop
!= NULL
) && (prefsPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1044 _SCErrorSet(kSCStatusInvalidArgument
);
1048 if (runLoop
== NULL
) {
1049 SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, NULL
);
1050 dispatch_release(prefsPrivate
->dispatchQueue
);
1051 prefsPrivate
->dispatchQueue
= NULL
;
1053 if (!_SC_unschedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
, FALSE
)) {
1054 // if not currently scheduled on this runLoop / runLoopMode
1055 _SCErrorSet(kSCStatusInvalidArgument
);
1059 n
= CFArrayGetCount(prefsPrivate
->rlList
);
1060 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
1062 * if we are no longer scheduled to receive notifications for
1063 * this runLoop / runLoopMode
1065 CFRunLoopRemoveSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
1068 // if *all* notifications have been unscheduled
1069 CFRelease(prefsPrivate
->rlList
);
1070 prefsPrivate
->rlList
= NULL
;
1071 CFRunLoopSourceInvalidate(prefsPrivate
->rls
);
1072 CFRelease(prefsPrivate
->rls
);
1073 prefsPrivate
->rls
= NULL
;
1079 CFArrayRef changedKeys
;
1081 SC_log(LOG_DEBUG
, "unscheduled");
1083 // if *all* notifications have been unscheduled
1084 prefsPrivate
->scheduled
= FALSE
;
1086 // no need to track changes
1087 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
1089 // clear out any pending notifications
1090 changedKeys
= SCDynamicStoreCopyNotifiedKeys(prefsPrivate
->session
);
1091 if (changedKeys
!= NULL
) {
1092 CFRelease(changedKeys
);
1095 // remove SCDynamicStore session, release 'prefs' reference
1096 __SCPreferencesRemoveSession(prefs
);
1103 pthread_mutex_unlock(&prefsPrivate
->lock
);
1109 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
1110 CFRunLoopRef runLoop
,
1111 CFStringRef runLoopMode
)
1113 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1114 _SCErrorSet(kSCStatusInvalidArgument
);
1118 return __SCPreferencesScheduleWithRunLoop(prefs
, runLoop
, runLoopMode
, NULL
);
1123 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
1124 CFRunLoopRef runLoop
,
1125 CFStringRef runLoopMode
)
1127 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1128 _SCErrorSet(kSCStatusInvalidArgument
);
1132 return __SCPreferencesUnscheduleFromRunLoop(prefs
, runLoop
, runLoopMode
);
1137 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs
,
1138 dispatch_queue_t queue
)
1142 if (!isA_SCPreferences(prefs
)) {
1143 /* sorry, you must provide a session */
1144 _SCErrorSet(kSCStatusNoPrefsSession
);
1148 if (queue
!= NULL
) {
1149 ok
= __SCPreferencesScheduleWithRunLoop(prefs
, NULL
, NULL
, queue
);
1151 ok
= __SCPreferencesUnscheduleFromRunLoop(prefs
, NULL
, NULL
);
1159 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs
)
1162 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1163 uint32_t status
= kSCStatusOK
;
1165 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
1170 // have the helper "synchronize" the prefs
1171 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
1172 SCHELPER_MSG_PREFS_SYNCHRONIZE
,
1178 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
1179 _SCHelperClose(&prefsPrivate
->helper_port
);
1188 SCPreferencesSynchronize(SCPreferencesRef prefs
)
1190 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1192 if (!isA_SCPreferences(prefs
)) {
1193 /* sorry, you must provide a session */
1194 _SCErrorSet(kSCStatusNoPrefsSession
);
1198 SC_log(LOG_DEBUG
, "SCPreferences() synchronize: %s",
1199 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
1201 if (prefsPrivate
->authorizationData
!= NULL
) {
1202 __SCPreferencesSynchronize_helper(prefs
);
1204 if (prefsPrivate
->prefs
!= NULL
) {
1205 CFRelease(prefsPrivate
->prefs
);
1206 prefsPrivate
->prefs
= NULL
;
1208 if (prefsPrivate
->signature
!= NULL
) {
1209 CFRelease(prefsPrivate
->signature
);
1210 prefsPrivate
->signature
= NULL
;
1212 prefsPrivate
->accessed
= FALSE
;
1213 prefsPrivate
->changed
= FALSE
;