2 * Copyright(c) 2000-2017 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 <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
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 = %@"), prefsPrivate
->prefsID
);
86 CFStringAppendFormat(result
, NULL
, CFSTR(", path = %s"),
87 prefsPrivate
->newPath
? 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 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)cf
;
111 SC_log(LOG_DEBUG
, "release %@", prefsPrivate
);
113 /* release resources */
115 pthread_mutex_destroy(&prefsPrivate
->lock
);
117 if (prefsPrivate
->name
) CFRelease(prefsPrivate
->name
);
118 if (prefsPrivate
->prefsID
) CFRelease(prefsPrivate
->prefsID
);
119 if (prefsPrivate
->options
) CFRelease(prefsPrivate
->options
);
120 if (prefsPrivate
->path
) CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
121 if (prefsPrivate
->newPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->newPath
);
122 if (prefsPrivate
->lockFD
!= -1) {
123 if (prefsPrivate
->lockPath
!= NULL
) {
124 unlink(prefsPrivate
->lockPath
);
126 close(prefsPrivate
->lockFD
);
128 if (prefsPrivate
->lockPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->lockPath
);
129 if (prefsPrivate
->signature
) CFRelease(prefsPrivate
->signature
);
130 if (prefsPrivate
->sessionNoO_EXLOCK
!= NULL
) {
131 CFRelease(prefsPrivate
->sessionNoO_EXLOCK
);
133 if (prefsPrivate
->sessionKeyLock
) CFRelease(prefsPrivate
->sessionKeyLock
);
134 if (prefsPrivate
->sessionKeyCommit
) CFRelease(prefsPrivate
->sessionKeyCommit
);
135 if (prefsPrivate
->sessionKeyApply
) CFRelease(prefsPrivate
->sessionKeyApply
);
136 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
137 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
139 if (prefsPrivate
->prefs
) CFRelease(prefsPrivate
->prefs
);
140 if (prefsPrivate
->authorizationData
!= NULL
) CFRelease(prefsPrivate
->authorizationData
);
141 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
142 (void) _SCHelperExec(prefsPrivate
->helper_port
,
143 SCHELPER_MSG_PREFS_CLOSE
,
147 _SCHelperClose(&prefsPrivate
->helper_port
);
154 static CFTypeID __kSCPreferencesTypeID
= _kCFRuntimeNotATypeID
;
157 static const CFRuntimeClass __SCPreferencesClass
= {
159 "SCPreferences", // className
162 __SCPreferencesDeallocate
, // dealloc
165 NULL
, // copyFormattingDesc
166 __SCPreferencesCopyDescription
// copyDebugDesc
170 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
173 __SCPreferencesInitialize(void) {
174 /* register with CoreFoundation */
175 __kSCPreferencesTypeID
= _CFRuntimeRegisterClass(&__SCPreferencesClass
);
180 static SCPreferencesPrivateRef
181 __SCPreferencesCreatePrivate(CFAllocatorRef allocator
)
183 SCPreferencesPrivateRef prefsPrivate
;
186 /* initialize runtime */
187 pthread_once(&initialized
, __SCPreferencesInitialize
);
189 /* allocate prefs session */
190 size
= sizeof(SCPreferencesPrivate
) - sizeof(CFRuntimeBase
);
191 prefsPrivate
= (SCPreferencesPrivateRef
)_CFRuntimeCreateInstance(allocator
,
192 __kSCPreferencesTypeID
,
195 if (prefsPrivate
== NULL
) {
199 /* initialize non-zero/NULL members */
200 pthread_mutex_init(&prefsPrivate
->lock
, NULL
);
201 prefsPrivate
->lockFD
= -1;
202 prefsPrivate
->isRoot
= (geteuid() == 0);
208 __private_extern__ Boolean
209 __SCPreferencesCreate_helper(SCPreferencesRef prefs
)
211 CFDataRef data
= NULL
;
212 CFMutableDictionaryRef info
;
215 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
216 uint32_t status
= kSCStatusOK
;
218 uint32_t pid
= getpid();
221 ok
= _SCHelperOpen(prefsPrivate
->authorizationData
,
222 &prefsPrivate
->helper_port
);
227 // create a dictionary of information to pass to the helper
228 info
= CFDictionaryCreateMutable(NULL
,
230 &kCFTypeDictionaryKeyCallBacks
,
231 &kCFTypeDictionaryValueCallBacks
);
234 if (prefsPrivate
->prefsID
!= NULL
) {
235 CFDictionarySetValue(info
, CFSTR("prefsID"), prefsPrivate
->prefsID
);
239 if (prefsPrivate
->options
!= NULL
) {
240 CFDictionarySetValue(info
, CFSTR("options"), prefsPrivate
->options
);
243 // save preferences session "name"
244 CFDictionarySetValue(info
, CFSTR("name"), prefsPrivate
->name
);
247 num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &pid
);
248 CFDictionarySetValue(info
, CFSTR("PID"), num
);
252 str
= CFStringCreateWithCString(NULL
, getprogname(), kCFStringEncodingUTF8
);
253 CFDictionarySetValue(info
, CFSTR("PROC_NAME"), str
);
256 // serialize the info
257 ok
= _SCSerialize(info
, &data
, NULL
, NULL
);
259 if (data
== NULL
|| !ok
) {
263 // have the helper "open" the prefs
264 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
265 SCHELPER_MSG_PREFS_OPEN
,
269 if (data
!= NULL
) CFRelease(data
);
274 if (status
!= kSCStatusOK
) {
283 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
284 _SCHelperClose(&prefsPrivate
->helper_port
);
287 status
= kSCStatusAccessError
;
298 __SCPreferencesAccess_helper(SCPreferencesRef prefs
)
301 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
302 CFDictionaryRef serverDict
= NULL
;
303 CFDictionaryRef serverPrefs
= NULL
;
304 CFDictionaryRef serverSignature
= NULL
;
305 uint32_t status
= kSCStatusOK
;
306 CFDataRef reply
= NULL
;
308 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
309 ok
= __SCPreferencesCreate_helper(prefs
);
315 // have the helper "access" the prefs
316 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
317 SCHELPER_MSG_PREFS_ACCESS
,
325 if (status
!= kSCStatusOK
) {
333 ok
= _SCUnserialize((CFPropertyListRef
*)&serverDict
, reply
, NULL
, 0);
339 if (isA_CFDictionary(serverDict
)) {
340 serverPrefs
= CFDictionaryGetValue(serverDict
, CFSTR("preferences"));
341 serverPrefs
= isA_CFDictionary(serverPrefs
);
343 serverSignature
= CFDictionaryGetValue(serverDict
, CFSTR("signature"));
344 serverSignature
= isA_CFData(serverSignature
);
347 if ((serverPrefs
== NULL
) || (serverSignature
== NULL
)) {
348 if (serverDict
!= NULL
) CFRelease(serverDict
);
352 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, serverPrefs
);
353 prefsPrivate
->signature
= CFRetain(serverSignature
);
354 prefsPrivate
->accessed
= TRUE
;
355 CFRelease(serverDict
);
362 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
363 _SCHelperClose(&prefsPrivate
->helper_port
);
366 status
= kSCStatusAccessError
;
376 static SCPreferencesPrivateRef
377 __SCPreferencesCreate(CFAllocatorRef allocator
,
380 CFDataRef authorizationData
,
381 CFDictionaryRef options
)
383 SCPreferencesPrivateRef prefsPrivate
;
384 int sc_status
= kSCStatusOK
;
387 * allocate and initialize a new prefs session
389 prefsPrivate
= __SCPreferencesCreatePrivate(allocator
);
390 if (prefsPrivate
== NULL
) {
394 prefsPrivate
->name
= CFStringCreateCopy(allocator
, name
);
395 if (prefsID
!= NULL
) {
396 prefsPrivate
->prefsID
= CFStringCreateCopy(allocator
, prefsID
);
398 if (authorizationData
!= NULL
) {
399 prefsPrivate
->authorizationData
= CFRetain(authorizationData
);
401 if (options
!= NULL
) {
402 prefsPrivate
->options
= CFDictionaryCreateCopy(allocator
, options
);
408 * convert prefsID to path
410 prefsPrivate
->path
= __SCPreferencesPath(allocator
,
412 (prefsPrivate
->newPath
== NULL
));
413 if (prefsPrivate
->path
== NULL
) {
414 sc_status
= kSCStatusFailed
;
418 if (access(prefsPrivate
->path
, R_OK
) == 0) {
425 if ((prefsID
== NULL
) || !CFStringHasPrefix(prefsID
, CFSTR("/"))) {
426 /* if default preference ID or relative path */
427 if (prefsPrivate
->newPath
== NULL
) {
429 * we've looked in the "new" prefs directory
430 * without success. Save the "new" path and
431 * look in the "old" prefs directory.
433 prefsPrivate
->newPath
= prefsPrivate
->path
;
437 * we've looked in both the "new" and "old"
438 * prefs directories without success. Use
441 CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
442 prefsPrivate
->path
= prefsPrivate
->newPath
;
443 prefsPrivate
->newPath
= NULL
;
447 /* no preference data, start fresh */
448 sc_status
= kSCStatusNoConfigFile
;
452 if (prefsPrivate
->authorizationData
!= NULL
) {
453 /* no problem, we'll be using the helper */
457 SC_log(LOG_INFO
, "open() failed: %s", strerror(errno
));
458 sc_status
= kSCStatusAccessError
;
461 SC_log(LOG_INFO
, "open() failed: %s", strerror(errno
));
462 sc_status
= kSCStatusFailed
;
468 CFRelease(prefsPrivate
);
469 _SCErrorSet(sc_status
);
475 _SCErrorSet(sc_status
);
480 __private_extern__
void
481 __SCPreferencesAccess(SCPreferencesRef prefs
)
483 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
485 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
488 if (prefsPrivate
->accessed
) {
489 // if preference data has already been accessed
493 if (access(prefsPrivate
->path
, R_OK
) == 0) {
494 fd
= open(prefsPrivate
->path
, O_RDONLY
, 0644);
500 if (fstat(fd
, &statBuf
) == -1) {
501 SC_log(LOG_NOTICE
, "fstat() failed: %s", strerror(errno
));
502 bzero(&statBuf
, sizeof(statBuf
));
507 /* no preference data, start fresh */
511 if (prefsPrivate
->authorizationData
!= NULL
) {
512 if (__SCPreferencesAccess_helper(prefs
)) {
515 SC_log(LOG_NOTICE
, "__SCPreferencesAccess_helper() failed: %s",
516 SCErrorString(SCError()));
522 SC_log(LOG_NOTICE
, "open() failed: %s", strerror(errno
));
525 bzero(&statBuf
, sizeof(statBuf
));
528 if (prefsPrivate
->signature
!= NULL
) CFRelease(prefsPrivate
->signature
);
529 prefsPrivate
->signature
= __SCPSignatureFromStatbuf(&statBuf
);
531 if (statBuf
.st_size
> 0) {
532 CFDictionaryRef dict
;
533 CFErrorRef error
= NULL
;
534 CFMutableDataRef xmlData
;
537 * extract property list
539 xmlData
= CFDataCreateMutable(allocator
, (CFIndex
)statBuf
.st_size
);
540 CFDataSetLength(xmlData
, (CFIndex
)statBuf
.st_size
);
541 if (read(fd
, (void *)CFDataGetBytePtr(xmlData
), (CFIndex
)statBuf
.st_size
) != (CFIndex
)statBuf
.st_size
) {
542 /* corrupt prefs file, start fresh */
543 SC_log(LOG_INFO
, "read(): could not load preference data");
552 dict
= CFPropertyListCreateWithData(allocator
, xmlData
, kCFPropertyListImmutable
, NULL
, &error
);
555 /* corrupt prefs file, start fresh */
557 SC_log(LOG_NOTICE
, "CFPropertyListCreateWithData(): %@", error
);
564 * make sure that we've got a dictionary
566 if (!isA_CFDictionary(dict
)) {
567 /* corrupt prefs file, start fresh */
568 SC_log(LOG_INFO
, "CFGetTypeID(): not a dictionary");
573 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(allocator
, 0, dict
);
583 if (prefsPrivate
->prefs
== NULL
) {
585 * new file, create empty preferences
587 // SC_log(LOG_INFO, "creating new preferences file");
588 prefsPrivate
->prefs
= CFDictionaryCreateMutable(allocator
,
590 &kCFTypeDictionaryKeyCallBacks
,
591 &kCFTypeDictionaryValueCallBacks
);
592 prefsPrivate
->changed
= TRUE
;
595 SC_log(LOG_DEBUG
, "SCPreferences() access: %s",
596 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
598 prefsPrivate
->accessed
= TRUE
;
604 SCPreferencesCreate(CFAllocatorRef allocator
,
608 SCPreferencesPrivateRef prefsPrivate
;
610 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, NULL
, NULL
);
611 if (prefsPrivate
!= NULL
) {
612 SC_log(LOG_DEBUG
, "create %@", prefsPrivate
);
615 return (SCPreferencesRef
)prefsPrivate
;
620 SCPreferencesCreateWithAuthorization(CFAllocatorRef allocator
,
623 AuthorizationRef authorization
)
625 SCPreferencesRef prefs
;
627 #if !TARGET_OS_IPHONE
628 if (authorization
== NULL
) {
629 authorization
= kSCPreferencesUseEntitlementAuthorization
;
631 #else // !TARGET_OS_IPHONE
632 authorization
= kSCPreferencesUseEntitlementAuthorization
;
633 #endif // !TARGET_OS_IPHONE
635 prefs
= SCPreferencesCreateWithOptions(allocator
, name
, prefsID
, authorization
, NULL
);
641 SCPreferencesCreateWithOptions(CFAllocatorRef allocator
,
644 AuthorizationRef authorization
,
645 CFDictionaryRef options
)
647 CFDataRef authorizationData
= NULL
;
648 SCPreferencesPrivateRef prefsPrivate
;
650 if (options
!= NULL
) {
651 if (!isA_CFDictionary(options
)) {
652 _SCErrorSet(kSCStatusInvalidArgument
);
657 if (authorization
!= NULL
) {
658 CFMutableDictionaryRef authorizationDict
;
660 CFStringRef bundleID
= NULL
;
662 authorizationDict
= CFDictionaryCreateMutable(NULL
,
664 &kCFTypeDictionaryKeyCallBacks
,
665 &kCFTypeDictionaryValueCallBacks
);
666 #if !TARGET_OS_IPHONE
667 if (authorization
!= kSCPreferencesUseEntitlementAuthorization
) {
669 AuthorizationExternalForm extForm
;
672 os_status
= AuthorizationMakeExternalForm(authorization
, &extForm
);
673 if (os_status
!= errAuthorizationSuccess
) {
674 SC_log(LOG_INFO
, "AuthorizationMakeExternalForm() failed");
675 _SCErrorSet(kSCStatusInvalidArgument
);
676 CFRelease(authorizationDict
);
680 data
= CFDataCreate(NULL
, (const UInt8
*)extForm
.bytes
, sizeof(extForm
.bytes
));
681 CFDictionaryAddValue(authorizationDict
,
682 kSCHelperAuthAuthorization
,
688 /* get the application/executable/bundle name */
689 bundle
= CFBundleGetMainBundle();
690 if (bundle
!= NULL
) {
691 bundleID
= CFBundleGetIdentifier(bundle
);
692 if (bundleID
!= NULL
) {
697 url
= CFBundleCopyExecutableURL(bundle
);
699 bundleID
= CFURLCopyPath(url
);
704 if (bundleID
!= NULL
) {
705 if (CFEqual(bundleID
, CFSTR("/"))) {
711 if (bundleID
== NULL
) {
712 bundleID
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Unknown(%d)"), getpid());
714 CFDictionaryAddValue(authorizationDict
,
715 kSCHelperAuthCallerInfo
,
719 if (authorizationDict
!= NULL
) {
720 (void) _SCSerialize((CFPropertyListRef
)authorizationDict
,
724 CFRelease(authorizationDict
);
728 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, authorizationData
, options
);
729 if (prefsPrivate
!= NULL
) {
730 const char *astr
= "";
731 const char *ostr
= "";
733 if (options
!= NULL
) {
737 if (authorization
!= NULL
) {
738 if (authorization
== kSCPreferencesUseEntitlementAuthorization
) {
739 astr
= "entitlement";
741 astr
= "authorization";
745 SC_log(LOG_DEBUG
, "create w/%s%s%s %@",
747 ((ostr
!= "") && (astr
!= "")) ? " + " : "",
752 if (authorizationData
!= NULL
) CFRelease(authorizationData
);
754 return (SCPreferencesRef
)prefsPrivate
;
759 SCPreferencesGetTypeID(void) {
760 pthread_once(&initialized
, __SCPreferencesInitialize
); /* initialize runtime */
761 return __kSCPreferencesTypeID
;
766 prefsNotify(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
768 #pragma unused(store)
770 void (*context_release
)(const void *);
773 SCPreferencesNotification notify
= 0;
774 SCPreferencesRef prefs
= (SCPreferencesRef
)info
;
775 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
776 SCPreferencesCallBack rlsFunction
;
778 n
= (changedKeys
!= NULL
) ? CFArrayGetCount(changedKeys
) : 0;
779 for (i
= 0; i
< n
; i
++) {
782 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
783 if (CFEqual(key
, prefsPrivate
->sessionKeyCommit
)) {
784 // if preferences have been saved
785 notify
|= kSCPreferencesNotificationCommit
;
787 if (CFEqual(key
, prefsPrivate
->sessionKeyApply
)) {
788 // if stored preferences should be applied to current configuration
789 notify
|= kSCPreferencesNotificationApply
;
798 pthread_mutex_lock(&prefsPrivate
->lock
);
801 rlsFunction
= prefsPrivate
->rlsFunction
;
802 if (prefsPrivate
->rlsContext
.retain
!= NULL
) {
803 context_info
= (void *)prefsPrivate
->rlsContext
.retain(prefsPrivate
->rlsContext
.info
);
804 context_release
= prefsPrivate
->rlsContext
.release
;
806 context_info
= prefsPrivate
->rlsContext
.info
;
807 context_release
= NULL
;
810 pthread_mutex_unlock(&prefsPrivate
->lock
);
812 if (rlsFunction
!= NULL
) {
813 SC_log(LOG_DEBUG
, "exec SCPreferences callout: %s%s%s",
814 ((notify
& kSCPreferencesNotificationCommit
) != 0) ? "commit" : "",
815 (((notify
& kSCPreferencesNotificationCommit
) != 0) &&
816 ((notify
& kSCPreferencesNotificationApply
) != 0)) ? ", " : "",
817 ((notify
& kSCPreferencesNotificationApply
) != 0) ? "apply" : "");
818 (*rlsFunction
)(prefs
, notify
, context_info
);
821 if (context_release
!= NULL
) {
822 (*context_release
)(context_info
);
829 __private_extern__
void
830 __SCPreferencesAddSessionKeys(SCPreferencesRef prefs
)
832 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
834 /* create the session "commit" key */
835 if (prefsPrivate
->sessionKeyCommit
== NULL
) {
836 prefsPrivate
->sessionKeyCommit
= _SCPNotificationKey(NULL
,
837 prefsPrivate
->prefsID
,
838 kSCPreferencesKeyCommit
);
841 /* create the session "apply" key */
842 if (prefsPrivate
->sessionKeyApply
== NULL
) {
843 prefsPrivate
->sessionKeyApply
= _SCPNotificationKey(NULL
,
844 prefsPrivate
->prefsID
,
845 kSCPreferencesKeyApply
);
852 __private_extern__ Boolean
853 __SCPreferencesAddSession(SCPreferencesRef prefs
)
855 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
856 SCDynamicStoreContext context
= { 0
862 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
864 if (prefsPrivate
->sessionRefcnt
== 0) {
865 /* establish a dynamic store session */
866 prefsPrivate
->session
= SCDynamicStoreCreate(allocator
,
870 if (prefsPrivate
->session
== NULL
) {
871 SC_log(LOG_INFO
, "SCDynamicStoreCreate() failed");
875 SC_log(LOG_DEBUG
, "added SCDynamicStore session (for prefs)");
878 prefsPrivate
->sessionRefcnt
++;
883 __private_extern__
void
884 __SCPreferencesRemoveSession(SCPreferencesRef prefs
)
886 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
888 if (prefsPrivate
->sessionRefcnt
> 0) {
889 if (--prefsPrivate
->sessionRefcnt
== 0) {
890 CFRelease(prefsPrivate
->session
);
891 prefsPrivate
->session
= NULL
;
893 SC_log(LOG_DEBUG
, "removed SCDynamicStore session (for prefs)");
902 SCPreferencesSetCallback(SCPreferencesRef prefs
,
903 SCPreferencesCallBack callout
,
904 SCPreferencesContext
*context
)
906 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
908 if (!isA_SCPreferences(prefs
)) {
909 /* sorry, you must provide a session */
910 _SCErrorSet(kSCStatusNoPrefsSession
);
914 pthread_mutex_lock(&prefsPrivate
->lock
);
916 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
917 /* let go of the current context */
918 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
921 prefsPrivate
->rlsFunction
= callout
;
922 prefsPrivate
->rlsContext
.info
= NULL
;
923 prefsPrivate
->rlsContext
.retain
= NULL
;
924 prefsPrivate
->rlsContext
.release
= NULL
;
925 prefsPrivate
->rlsContext
.copyDescription
= NULL
;
926 if (context
!= NULL
) {
927 bcopy(context
, &prefsPrivate
->rlsContext
, sizeof(SCPreferencesContext
));
928 if (context
->retain
!= NULL
) {
929 prefsPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
933 pthread_mutex_unlock(&prefsPrivate
->lock
);
940 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
941 CFRunLoopRef runLoop
,
942 CFStringRef runLoopMode
,
943 dispatch_queue_t queue
)
946 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
948 pthread_mutex_lock(&prefsPrivate
->lock
);
950 if ((prefsPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
951 ((queue
!= NULL
) && prefsPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
952 _SCErrorSet(kSCStatusInvalidArgument
);
956 if (!prefsPrivate
->scheduled
) {
957 CFMutableArrayRef keys
;
959 // add SCDynamicStore session (for notifications) ... and hold a 'prefs' reference
960 if (prefsPrivate
->session
== NULL
) {
961 ok
= __SCPreferencesAddSession(prefs
);
965 assert(prefsPrivate
->session
!= NULL
);
968 // add SCDynamicStore "keys"
969 __SCPreferencesAddSessionKeys(prefs
);
971 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
972 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyCommit
);
973 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyApply
);
974 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, keys
, NULL
);
977 if (runLoop
!= NULL
) {
978 prefsPrivate
->rls
= SCDynamicStoreCreateRunLoopSource(NULL
, prefsPrivate
->session
, 0);
979 prefsPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
982 SC_log(LOG_DEBUG
, "scheduled");
984 prefsPrivate
->scheduled
= TRUE
;
988 ok
= SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, queue
);
990 prefsPrivate
->scheduled
= FALSE
;
991 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
992 __SCPreferencesRemoveSession(prefs
);
996 prefsPrivate
->dispatchQueue
= queue
;
997 dispatch_retain(prefsPrivate
->dispatchQueue
);
999 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
1001 * if we do not already have notifications scheduled with
1002 * this runLoop / runLoopMode
1004 CFRunLoopAddSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
1007 _SC_schedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
);
1014 pthread_mutex_unlock(&prefsPrivate
->lock
);
1020 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
1021 CFRunLoopRef runLoop
,
1022 CFStringRef runLoopMode
)
1024 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1028 pthread_mutex_lock(&prefsPrivate
->lock
);
1030 if ((runLoop
!= NULL
) && !prefsPrivate
->scheduled
) { // if we should be scheduled (but are not)
1031 _SCErrorSet(kSCStatusInvalidArgument
);
1035 if (((runLoop
== NULL
) && (prefsPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
1036 ((runLoop
!= NULL
) && (prefsPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1037 _SCErrorSet(kSCStatusInvalidArgument
);
1041 if (runLoop
== NULL
) {
1042 SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, NULL
);
1043 dispatch_release(prefsPrivate
->dispatchQueue
);
1044 prefsPrivate
->dispatchQueue
= NULL
;
1046 if (!_SC_unschedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
, FALSE
)) {
1047 // if not currently scheduled on this runLoop / runLoopMode
1048 _SCErrorSet(kSCStatusInvalidArgument
);
1052 n
= CFArrayGetCount(prefsPrivate
->rlList
);
1053 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
1055 * if we are no longer scheduled to receive notifications for
1056 * this runLoop / runLoopMode
1058 CFRunLoopRemoveSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
1061 // if *all* notifications have been unscheduled
1062 CFRelease(prefsPrivate
->rlList
);
1063 prefsPrivate
->rlList
= NULL
;
1064 CFRunLoopSourceInvalidate(prefsPrivate
->rls
);
1065 CFRelease(prefsPrivate
->rls
);
1066 prefsPrivate
->rls
= NULL
;
1072 CFArrayRef changedKeys
;
1074 SC_log(LOG_DEBUG
, "unscheduled");
1076 // if *all* notifications have been unscheduled
1077 prefsPrivate
->scheduled
= FALSE
;
1079 // no need to track changes
1080 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
1082 // clear out any pending notifications
1083 changedKeys
= SCDynamicStoreCopyNotifiedKeys(prefsPrivate
->session
);
1084 if (changedKeys
!= NULL
) {
1085 CFRelease(changedKeys
);
1088 // remove SCDynamicStore session, release 'prefs' reference
1089 __SCPreferencesRemoveSession(prefs
);
1096 pthread_mutex_unlock(&prefsPrivate
->lock
);
1102 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
1103 CFRunLoopRef runLoop
,
1104 CFStringRef runLoopMode
)
1106 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1107 _SCErrorSet(kSCStatusInvalidArgument
);
1111 return __SCPreferencesScheduleWithRunLoop(prefs
, runLoop
, runLoopMode
, NULL
);
1116 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
1117 CFRunLoopRef runLoop
,
1118 CFStringRef runLoopMode
)
1120 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1121 _SCErrorSet(kSCStatusInvalidArgument
);
1125 return __SCPreferencesUnscheduleFromRunLoop(prefs
, runLoop
, runLoopMode
);
1130 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs
,
1131 dispatch_queue_t queue
)
1135 if (!isA_SCPreferences(prefs
)) {
1136 /* sorry, you must provide a session */
1137 _SCErrorSet(kSCStatusNoPrefsSession
);
1141 if (queue
!= NULL
) {
1142 ok
= __SCPreferencesScheduleWithRunLoop(prefs
, NULL
, NULL
, queue
);
1144 ok
= __SCPreferencesUnscheduleFromRunLoop(prefs
, NULL
, NULL
);
1152 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs
)
1155 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1156 uint32_t status
= kSCStatusOK
;
1158 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
1163 // have the helper "synchronize" the prefs
1164 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
1165 SCHELPER_MSG_PREFS_SYNCHRONIZE
,
1171 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
1172 _SCHelperClose(&prefsPrivate
->helper_port
);
1181 SCPreferencesSynchronize(SCPreferencesRef prefs
)
1183 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1185 if (!isA_SCPreferences(prefs
)) {
1186 /* sorry, you must provide a session */
1187 _SCErrorSet(kSCStatusNoPrefsSession
);
1191 SC_log(LOG_DEBUG
, "SCPreferences() synchronize: %s",
1192 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
1194 if (prefsPrivate
->authorizationData
!= NULL
) {
1195 __SCPreferencesSynchronize_helper(prefs
);
1197 if (prefsPrivate
->prefs
!= NULL
) {
1198 CFRelease(prefsPrivate
->prefs
);
1199 prefsPrivate
->prefs
= NULL
;
1201 if (prefsPrivate
->signature
!= NULL
) {
1202 CFRelease(prefsPrivate
->signature
);
1203 prefsPrivate
->signature
= NULL
;
1205 prefsPrivate
->accessed
= FALSE
;
1206 prefsPrivate
->changed
= FALSE
;