2 * Copyright(c) 2000-2015 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>
39 #include <sys/cdefs.h>
40 #include <dispatch/dispatch.h>
41 #include <SystemConfiguration/SystemConfiguration.h>
42 #include <SystemConfiguration/SCValidation.h>
43 #include <SystemConfiguration/SCPrivate.h>
44 #include "SCPreferencesInternal.h"
45 #include "SCHelper_client.h"
47 #include "dy_framework.h"
53 #include <sys/errno.h>
56 const AuthorizationRef kSCPreferencesUseEntitlementAuthorization
= (AuthorizationRef
)CFSTR("UseEntitlement");
59 static __inline__ CFTypeRef
60 isA_SCPreferences(CFTypeRef obj
)
62 return (isA_CFType(obj
, SCPreferencesGetTypeID()));
67 __SCPreferencesCopyDescription(CFTypeRef cf
) {
68 CFAllocatorRef allocator
= CFGetAllocator(cf
);
69 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)cf
;
70 CFMutableStringRef result
;
72 result
= CFStringCreateMutable(allocator
, 0);
73 CFStringAppendFormat(result
, NULL
, CFSTR("<SCPreferences %p [%p]> {"), cf
, allocator
);
74 CFStringAppendFormat(result
, NULL
, CFSTR("name = %@"), prefsPrivate
->name
);
75 CFStringAppendFormat(result
, NULL
, CFSTR(", id = %@"), prefsPrivate
->prefsID
);
76 CFStringAppendFormat(result
, NULL
, CFSTR(", path = %s"),
77 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
78 if (prefsPrivate
->accessed
) {
79 CFStringAppendFormat(result
, NULL
, CFSTR(", accessed"));
81 if (prefsPrivate
->changed
) {
82 CFStringAppendFormat(result
, NULL
, CFSTR(", changed"));
84 if (prefsPrivate
->locked
) {
85 CFStringAppendFormat(result
, NULL
, CFSTR(", locked"));
87 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
88 CFStringAppendFormat(result
, NULL
, CFSTR(", helper port = 0x%x"), prefsPrivate
->helper_port
);
90 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
97 __SCPreferencesDeallocate(CFTypeRef cf
)
99 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)cf
;
101 /* release resources */
103 pthread_mutex_destroy(&prefsPrivate
->lock
);
105 if (prefsPrivate
->name
) CFRelease(prefsPrivate
->name
);
106 if (prefsPrivate
->prefsID
) CFRelease(prefsPrivate
->prefsID
);
107 if (prefsPrivate
->options
) CFRelease(prefsPrivate
->options
);
108 if (prefsPrivate
->path
) CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
109 if (prefsPrivate
->newPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->newPath
);
110 if (prefsPrivate
->lockFD
!= -1) {
111 if (prefsPrivate
->lockPath
!= NULL
) {
112 unlink(prefsPrivate
->lockPath
);
114 close(prefsPrivate
->lockFD
);
116 if (prefsPrivate
->lockPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->lockPath
);
117 if (prefsPrivate
->signature
) CFRelease(prefsPrivate
->signature
);
118 if (prefsPrivate
->sessionNoO_EXLOCK
!= NULL
) {
119 CFRelease(prefsPrivate
->sessionNoO_EXLOCK
);
121 if (prefsPrivate
->sessionKeyLock
) CFRelease(prefsPrivate
->sessionKeyLock
);
122 if (prefsPrivate
->sessionKeyCommit
) CFRelease(prefsPrivate
->sessionKeyCommit
);
123 if (prefsPrivate
->sessionKeyApply
) CFRelease(prefsPrivate
->sessionKeyApply
);
124 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
125 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
127 if (prefsPrivate
->prefs
) CFRelease(prefsPrivate
->prefs
);
128 if (prefsPrivate
->authorizationData
!= NULL
) CFRelease(prefsPrivate
->authorizationData
);
129 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
130 (void) _SCHelperExec(prefsPrivate
->helper_port
,
131 SCHELPER_MSG_PREFS_CLOSE
,
135 _SCHelperClose(&prefsPrivate
->helper_port
);
142 static CFTypeID __kSCPreferencesTypeID
= _kCFRuntimeNotATypeID
;
145 static const CFRuntimeClass __SCPreferencesClass
= {
147 "SCPreferences", // className
150 __SCPreferencesDeallocate
, // dealloc
153 NULL
, // copyFormattingDesc
154 __SCPreferencesCopyDescription
// copyDebugDesc
158 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
161 __SCPreferencesInitialize(void) {
162 __kSCPreferencesTypeID
= _CFRuntimeRegisterClass(&__SCPreferencesClass
);
167 static SCPreferencesPrivateRef
168 __SCPreferencesCreatePrivate(CFAllocatorRef allocator
)
170 SCPreferencesPrivateRef prefsPrivate
;
173 /* initialize runtime */
174 pthread_once(&initialized
, __SCPreferencesInitialize
);
176 /* allocate prefs session */
177 size
= sizeof(SCPreferencesPrivate
) - sizeof(CFRuntimeBase
);
178 prefsPrivate
= (SCPreferencesPrivateRef
)_CFRuntimeCreateInstance(allocator
,
179 __kSCPreferencesTypeID
,
182 if (prefsPrivate
== NULL
) {
186 pthread_mutex_init(&prefsPrivate
->lock
, NULL
);
188 prefsPrivate
->name
= NULL
;
189 prefsPrivate
->prefsID
= NULL
;
190 prefsPrivate
->options
= NULL
;
191 prefsPrivate
->path
= NULL
;
192 prefsPrivate
->newPath
= NULL
; // new prefs path
193 prefsPrivate
->locked
= FALSE
;
194 prefsPrivate
->lockFD
= -1;
195 prefsPrivate
->lockPath
= NULL
;
196 prefsPrivate
->signature
= NULL
;
197 prefsPrivate
->session
= NULL
;
198 prefsPrivate
->sessionNoO_EXLOCK
= NULL
;
199 prefsPrivate
->sessionRefcnt
= 0;
200 prefsPrivate
->sessionKeyLock
= NULL
;
201 prefsPrivate
->sessionKeyCommit
= NULL
;
202 prefsPrivate
->sessionKeyApply
= NULL
;
203 prefsPrivate
->scheduled
= FALSE
;
204 prefsPrivate
->rls
= NULL
;
205 prefsPrivate
->rlsFunction
= NULL
;
206 prefsPrivate
->rlsContext
.info
= NULL
;
207 prefsPrivate
->rlsContext
.retain
= NULL
;
208 prefsPrivate
->rlsContext
.release
= NULL
;
209 prefsPrivate
->rlsContext
.copyDescription
= NULL
;
210 prefsPrivate
->rlList
= NULL
;
211 prefsPrivate
->dispatchQueue
= NULL
;
212 prefsPrivate
->prefs
= NULL
;
213 prefsPrivate
->accessed
= FALSE
;
214 prefsPrivate
->changed
= FALSE
;
215 prefsPrivate
->isRoot
= (geteuid() == 0);
216 prefsPrivate
->limit_SCNetworkConfiguration
= FALSE
;
217 prefsPrivate
->authorizationData
= NULL
;
218 prefsPrivate
->helper_port
= MACH_PORT_NULL
;
224 __private_extern__ Boolean
225 __SCPreferencesCreate_helper(SCPreferencesRef prefs
)
227 CFDataRef data
= NULL
;
228 CFMutableDictionaryRef info
;
231 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
232 uint32_t status
= kSCStatusOK
;
234 uint32_t pid
= getpid();
237 ok
= _SCHelperOpen(prefsPrivate
->authorizationData
,
238 &prefsPrivate
->helper_port
);
243 // create a dictionary of information to pass to the helper
244 info
= CFDictionaryCreateMutable(NULL
,
246 &kCFTypeDictionaryKeyCallBacks
,
247 &kCFTypeDictionaryValueCallBacks
);
250 if (prefsPrivate
->prefsID
!= NULL
) {
251 CFDictionarySetValue(info
, CFSTR("prefsID"), prefsPrivate
->prefsID
);
255 if (prefsPrivate
->options
!= NULL
) {
256 CFDictionarySetValue(info
, CFSTR("options"), prefsPrivate
->options
);
259 // save preferences session "name"
260 CFDictionarySetValue(info
, CFSTR("name"), prefsPrivate
->name
);
263 num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &pid
);
264 CFDictionarySetValue(info
, CFSTR("PID"), num
);
268 str
= CFStringCreateWithCString(NULL
, getprogname(), kCFStringEncodingUTF8
);
269 CFDictionarySetValue(info
, CFSTR("PROC_NAME"), str
);
272 // serialize the info
273 ok
= _SCSerialize(info
, &data
, NULL
, NULL
);
275 if (data
== NULL
|| !ok
) {
279 // have the helper "open" the prefs
280 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
281 SCHELPER_MSG_PREFS_OPEN
,
285 if (data
!= NULL
) CFRelease(data
);
290 if (status
!= kSCStatusOK
) {
299 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
300 _SCHelperClose(&prefsPrivate
->helper_port
);
303 status
= kSCStatusAccessError
;
314 __SCPreferencesAccess_helper(SCPreferencesRef prefs
)
317 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
318 CFDictionaryRef serverDict
= NULL
;
319 CFDictionaryRef serverPrefs
= NULL
;
320 CFDictionaryRef serverSignature
= NULL
;
321 uint32_t status
= kSCStatusOK
;
322 CFDataRef reply
= NULL
;
324 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
325 ok
= __SCPreferencesCreate_helper(prefs
);
331 // have the helper "access" the prefs
332 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
333 SCHELPER_MSG_PREFS_ACCESS
,
341 if (status
!= kSCStatusOK
) {
349 ok
= _SCUnserialize((CFPropertyListRef
*)&serverDict
, reply
, NULL
, 0);
355 if (isA_CFDictionary(serverDict
)) {
356 serverPrefs
= CFDictionaryGetValue(serverDict
, CFSTR("preferences"));
357 serverPrefs
= isA_CFDictionary(serverPrefs
);
359 serverSignature
= CFDictionaryGetValue(serverDict
, CFSTR("signature"));
360 serverSignature
= isA_CFData(serverSignature
);
363 if ((serverPrefs
== NULL
) || (serverSignature
== NULL
)) {
364 if (serverDict
!= NULL
) CFRelease(serverDict
);
368 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, serverPrefs
);
369 prefsPrivate
->signature
= CFRetain(serverSignature
);
370 prefsPrivate
->accessed
= TRUE
;
371 CFRelease(serverDict
);
378 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
379 _SCHelperClose(&prefsPrivate
->helper_port
);
382 status
= kSCStatusAccessError
;
392 static SCPreferencesPrivateRef
393 __SCPreferencesCreate(CFAllocatorRef allocator
,
396 CFDataRef authorizationData
,
397 CFDictionaryRef options
)
399 SCPreferencesPrivateRef prefsPrivate
;
400 int sc_status
= kSCStatusOK
;
403 * allocate and initialize a new prefs session
405 prefsPrivate
= __SCPreferencesCreatePrivate(allocator
);
406 if (prefsPrivate
== NULL
) {
410 prefsPrivate
->name
= CFStringCreateCopy(allocator
, name
);
411 if (prefsID
!= NULL
) {
412 prefsPrivate
->prefsID
= CFStringCreateCopy(allocator
, prefsID
);
414 if (authorizationData
!= NULL
) {
415 prefsPrivate
->authorizationData
= CFRetain(authorizationData
);
417 if (options
!= NULL
) {
418 prefsPrivate
->options
= CFDictionaryCreateCopy(allocator
, options
);
424 * convert prefsID to path
426 prefsPrivate
->path
= __SCPreferencesPath(allocator
,
428 (prefsPrivate
->newPath
== NULL
));
429 if (prefsPrivate
->path
== NULL
) {
430 sc_status
= kSCStatusFailed
;
434 if (access(prefsPrivate
->path
, R_OK
) == 0) {
441 if ((prefsID
== NULL
) || !CFStringHasPrefix(prefsID
, CFSTR("/"))) {
442 /* if default preference ID or relative path */
443 if (prefsPrivate
->newPath
== NULL
) {
445 * we've looked in the "new" prefs directory
446 * without success. Save the "new" path and
447 * look in the "old" prefs directory.
449 prefsPrivate
->newPath
= prefsPrivate
->path
;
453 * we've looked in both the "new" and "old"
454 * prefs directories without success. Use
457 CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
458 prefsPrivate
->path
= prefsPrivate
->newPath
;
459 prefsPrivate
->newPath
= NULL
;
463 /* no preference data, start fresh */
464 sc_status
= kSCStatusNoConfigFile
;
468 if (prefsPrivate
->authorizationData
!= NULL
) {
469 /* no problem, we'll be using the helper */
473 SC_log(LOG_INFO
, "open() failed: %s", strerror(errno
));
474 sc_status
= kSCStatusAccessError
;
477 SC_log(LOG_INFO
, "open() failed: %s", strerror(errno
));
478 sc_status
= kSCStatusFailed
;
484 CFRelease(prefsPrivate
);
485 _SCErrorSet(sc_status
);
491 _SCErrorSet(sc_status
);
496 __private_extern__
void
497 __SCPreferencesAccess(SCPreferencesRef prefs
)
499 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
501 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
504 if (prefsPrivate
->accessed
) {
505 // if preference data has already been accessed
509 if (access(prefsPrivate
->path
, R_OK
) == 0) {
510 fd
= open(prefsPrivate
->path
, O_RDONLY
, 0644);
516 if (fstat(fd
, &statBuf
) == -1) {
517 SC_log(LOG_NOTICE
, "fstat() failed: %s", strerror(errno
));
518 bzero(&statBuf
, sizeof(statBuf
));
523 /* no preference data, start fresh */
527 if (prefsPrivate
->authorizationData
!= NULL
) {
528 if (__SCPreferencesAccess_helper(prefs
)) {
531 SC_log(LOG_NOTICE
, "__SCPreferencesAccess_helper() failed: %s",
532 SCErrorString(SCError()));
538 SC_log(LOG_NOTICE
, "open() failed: %s", strerror(errno
));
541 bzero(&statBuf
, sizeof(statBuf
));
544 if (prefsPrivate
->signature
!= NULL
) CFRelease(prefsPrivate
->signature
);
545 prefsPrivate
->signature
= __SCPSignatureFromStatbuf(&statBuf
);
547 if (statBuf
.st_size
> 0) {
548 CFDictionaryRef dict
;
549 CFErrorRef error
= NULL
;
550 CFMutableDataRef xmlData
;
553 * extract property list
555 xmlData
= CFDataCreateMutable(allocator
, (CFIndex
)statBuf
.st_size
);
556 CFDataSetLength(xmlData
, (CFIndex
)statBuf
.st_size
);
557 if (read(fd
, (void *)CFDataGetBytePtr(xmlData
), (CFIndex
)statBuf
.st_size
) != (CFIndex
)statBuf
.st_size
) {
558 /* corrupt prefs file, start fresh */
559 SC_log(LOG_INFO
, "read(): could not load preference data");
568 dict
= CFPropertyListCreateWithData(allocator
, xmlData
, kCFPropertyListImmutable
, NULL
, &error
);
571 /* corrupt prefs file, start fresh */
573 SC_log(LOG_NOTICE
, "CFPropertyListCreateWithData(): %@", error
);
580 * make sure that we've got a dictionary
582 if (!isA_CFDictionary(dict
)) {
583 /* corrupt prefs file, start fresh */
584 SC_log(LOG_INFO
, "CFGetTypeID(): not a dictionary");
589 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(allocator
, 0, dict
);
599 if (prefsPrivate
->prefs
== NULL
) {
601 * new file, create empty preferences
603 // SC_log(LOG_INFO, "creating new preferences file");
604 prefsPrivate
->prefs
= CFDictionaryCreateMutable(allocator
,
606 &kCFTypeDictionaryKeyCallBacks
,
607 &kCFTypeDictionaryValueCallBacks
);
608 prefsPrivate
->changed
= TRUE
;
611 prefsPrivate
->accessed
= TRUE
;
617 SCPreferencesCreate(CFAllocatorRef allocator
,
621 SCPreferencesPrivateRef prefsPrivate
;
623 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, NULL
, NULL
);
624 return (SCPreferencesRef
)prefsPrivate
;
629 SCPreferencesCreateWithAuthorization(CFAllocatorRef allocator
,
632 AuthorizationRef authorization
)
634 SCPreferencesRef prefs
;
636 #if !TARGET_OS_IPHONE
637 if (authorization
== NULL
) {
638 authorization
= kSCPreferencesUseEntitlementAuthorization
;
640 #else // !TARGET_OS_IPHONE
641 authorization
= kSCPreferencesUseEntitlementAuthorization
;
642 #endif // !TARGET_OS_IPHONE
644 prefs
= SCPreferencesCreateWithOptions(allocator
, name
, prefsID
, authorization
, NULL
);
650 SCPreferencesCreateWithOptions(CFAllocatorRef allocator
,
653 AuthorizationRef authorization
,
654 CFDictionaryRef options
)
656 CFDataRef authorizationData
= NULL
;
657 SCPreferencesPrivateRef prefsPrivate
;
659 if (options
!= NULL
) {
660 if (!isA_CFDictionary(options
)) {
661 _SCErrorSet(kSCStatusInvalidArgument
);
666 if (authorization
!= NULL
) {
667 CFMutableDictionaryRef authorizationDict
;
669 CFStringRef bundleID
= NULL
;
671 authorizationDict
= CFDictionaryCreateMutable(NULL
,
673 &kCFTypeDictionaryKeyCallBacks
,
674 &kCFTypeDictionaryValueCallBacks
);
675 #if !TARGET_OS_IPHONE
676 if (authorization
!= kSCPreferencesUseEntitlementAuthorization
) {
678 AuthorizationExternalForm extForm
;
681 os_status
= AuthorizationMakeExternalForm(authorization
, &extForm
);
682 if (os_status
!= errAuthorizationSuccess
) {
683 SC_log(LOG_INFO
, "AuthorizationMakeExternalForm() failed");
684 _SCErrorSet(kSCStatusInvalidArgument
);
685 CFRelease(authorizationDict
);
689 data
= CFDataCreate(NULL
, (const UInt8
*)extForm
.bytes
, sizeof(extForm
.bytes
));
690 CFDictionaryAddValue(authorizationDict
,
691 kSCHelperAuthAuthorization
,
697 /* get the application/executable/bundle name */
698 bundle
= CFBundleGetMainBundle();
699 if (bundle
!= NULL
) {
700 bundleID
= CFBundleGetIdentifier(bundle
);
701 if (bundleID
!= NULL
) {
706 url
= CFBundleCopyExecutableURL(bundle
);
708 bundleID
= CFURLCopyPath(url
);
713 if (bundleID
!= NULL
) {
714 if (CFEqual(bundleID
, CFSTR("/"))) {
720 if (bundleID
== NULL
) {
721 bundleID
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Unknown(%d)"), getpid());
723 CFDictionaryAddValue(authorizationDict
,
724 kSCHelperAuthCallerInfo
,
728 if (authorizationDict
!= NULL
) {
729 _SCSerialize((CFPropertyListRef
)authorizationDict
,
733 CFRelease(authorizationDict
);
737 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, authorizationData
, options
);
738 if (authorizationData
!= NULL
) CFRelease(authorizationData
);
740 return (SCPreferencesRef
)prefsPrivate
;
745 SCPreferencesGetTypeID(void) {
746 pthread_once(&initialized
, __SCPreferencesInitialize
); /* initialize runtime */
747 return __kSCPreferencesTypeID
;
752 prefsNotify(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
755 void (*context_release
)(const void *);
758 SCPreferencesNotification notify
= 0;
759 SCPreferencesRef prefs
= (SCPreferencesRef
)info
;
760 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
761 SCPreferencesCallBack rlsFunction
;
763 n
= (changedKeys
!= NULL
) ? CFArrayGetCount(changedKeys
) : 0;
764 for (i
= 0; i
< n
; i
++) {
767 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
768 if (CFEqual(key
, prefsPrivate
->sessionKeyCommit
)) {
769 // if preferences have been saved
770 notify
|= kSCPreferencesNotificationCommit
;
772 if (CFEqual(key
, prefsPrivate
->sessionKeyApply
)) {
773 // if stored preferences should be applied to current configuration
774 notify
|= kSCPreferencesNotificationApply
;
783 pthread_mutex_lock(&prefsPrivate
->lock
);
786 rlsFunction
= prefsPrivate
->rlsFunction
;
787 if (prefsPrivate
->rlsContext
.retain
!= NULL
) {
788 context_info
= (void *)prefsPrivate
->rlsContext
.retain(prefsPrivate
->rlsContext
.info
);
789 context_release
= prefsPrivate
->rlsContext
.release
;
791 context_info
= prefsPrivate
->rlsContext
.info
;
792 context_release
= NULL
;
795 pthread_mutex_unlock(&prefsPrivate
->lock
);
797 if (rlsFunction
!= NULL
) {
798 (*rlsFunction
)(prefs
, notify
, context_info
);
801 if (context_release
!= NULL
) {
802 (*context_release
)(context_info
);
809 __private_extern__
void
810 __SCPreferencesAddSessionKeys(SCPreferencesRef prefs
)
812 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
814 /* create the session "commit" key */
815 if (prefsPrivate
->sessionKeyCommit
== NULL
) {
816 prefsPrivate
->sessionKeyCommit
= _SCPNotificationKey(NULL
,
817 prefsPrivate
->prefsID
,
818 kSCPreferencesKeyCommit
);
821 /* create the session "apply" key */
822 if (prefsPrivate
->sessionKeyApply
== NULL
) {
823 prefsPrivate
->sessionKeyApply
= _SCPNotificationKey(NULL
,
824 prefsPrivate
->prefsID
,
825 kSCPreferencesKeyApply
);
832 __private_extern__ Boolean
833 __SCPreferencesAddSession(SCPreferencesRef prefs
)
835 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
836 SCDynamicStoreContext context
= { 0
842 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
844 if (prefsPrivate
->sessionRefcnt
== 0) {
845 /* establish a dynamic store session */
846 prefsPrivate
->session
= SCDynamicStoreCreate(allocator
,
850 if (prefsPrivate
->session
== NULL
) {
851 SC_log(LOG_INFO
, "SCDynamicStoreCreate() failed");
856 prefsPrivate
->sessionRefcnt
++;
861 __private_extern__
void
862 __SCPreferencesRemoveSession(SCPreferencesRef prefs
)
864 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
866 if (prefsPrivate
->sessionRefcnt
> 0) {
867 if (--prefsPrivate
->sessionRefcnt
== 0) {
868 CFRelease(prefsPrivate
->session
);
869 prefsPrivate
->session
= NULL
;
878 SCPreferencesSetCallback(SCPreferencesRef prefs
,
879 SCPreferencesCallBack callout
,
880 SCPreferencesContext
*context
)
882 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
884 if (!isA_SCPreferences(prefs
)) {
885 /* sorry, you must provide a session */
886 _SCErrorSet(kSCStatusNoPrefsSession
);
890 pthread_mutex_lock(&prefsPrivate
->lock
);
892 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
893 /* let go of the current context */
894 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
897 prefsPrivate
->rlsFunction
= callout
;
898 prefsPrivate
->rlsContext
.info
= NULL
;
899 prefsPrivate
->rlsContext
.retain
= NULL
;
900 prefsPrivate
->rlsContext
.release
= NULL
;
901 prefsPrivate
->rlsContext
.copyDescription
= NULL
;
902 if (context
!= NULL
) {
903 bcopy(context
, &prefsPrivate
->rlsContext
, sizeof(SCPreferencesContext
));
904 if (context
->retain
!= NULL
) {
905 prefsPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
909 pthread_mutex_unlock(&prefsPrivate
->lock
);
916 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
917 CFRunLoopRef runLoop
,
918 CFStringRef runLoopMode
,
919 dispatch_queue_t queue
)
922 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
924 pthread_mutex_lock(&prefsPrivate
->lock
);
926 if ((prefsPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
927 ((queue
!= NULL
) && prefsPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
928 _SCErrorSet(kSCStatusInvalidArgument
);
932 if (!prefsPrivate
->scheduled
) {
933 CFMutableArrayRef keys
;
935 // add SCDynamicStore session (for notifications) ... and hold a 'prefs' reference
936 if (prefsPrivate
->session
== NULL
) {
937 ok
= __SCPreferencesAddSession(prefs
);
943 // add SCDynamicStore "keys"
944 __SCPreferencesAddSessionKeys(prefs
);
946 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
947 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyCommit
);
948 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyApply
);
949 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, keys
, NULL
);
952 if (runLoop
!= NULL
) {
953 prefsPrivate
->rls
= SCDynamicStoreCreateRunLoopSource(NULL
, prefsPrivate
->session
, 0);
954 prefsPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
957 prefsPrivate
->scheduled
= TRUE
;
961 ok
= SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, queue
);
963 prefsPrivate
->scheduled
= FALSE
;
964 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
965 __SCPreferencesRemoveSession(prefs
);
969 prefsPrivate
->dispatchQueue
= queue
;
970 dispatch_retain(prefsPrivate
->dispatchQueue
);
972 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
974 * if we do not already have notifications scheduled with
975 * this runLoop / runLoopMode
977 CFRunLoopAddSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
980 _SC_schedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
);
987 pthread_mutex_unlock(&prefsPrivate
->lock
);
993 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
994 CFRunLoopRef runLoop
,
995 CFStringRef runLoopMode
)
997 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1001 pthread_mutex_lock(&prefsPrivate
->lock
);
1003 if ((runLoop
!= NULL
) && !prefsPrivate
->scheduled
) { // if we should be scheduled (but are not)
1004 _SCErrorSet(kSCStatusInvalidArgument
);
1008 if (((runLoop
== NULL
) && (prefsPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
1009 ((runLoop
!= NULL
) && (prefsPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1010 _SCErrorSet(kSCStatusInvalidArgument
);
1014 if (runLoop
== NULL
) {
1015 SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, NULL
);
1016 dispatch_release(prefsPrivate
->dispatchQueue
);
1017 prefsPrivate
->dispatchQueue
= NULL
;
1019 if (!_SC_unschedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
, FALSE
)) {
1020 // if not currently scheduled on this runLoop / runLoopMode
1021 _SCErrorSet(kSCStatusInvalidArgument
);
1025 n
= CFArrayGetCount(prefsPrivate
->rlList
);
1026 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
1028 * if we are no longer scheduled to receive notifications for
1029 * this runLoop / runLoopMode
1031 CFRunLoopRemoveSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
1034 // if *all* notifications have been unscheduled
1035 CFRelease(prefsPrivate
->rlList
);
1036 prefsPrivate
->rlList
= NULL
;
1037 CFRunLoopSourceInvalidate(prefsPrivate
->rls
);
1038 CFRelease(prefsPrivate
->rls
);
1039 prefsPrivate
->rls
= NULL
;
1045 CFArrayRef changedKeys
;
1047 // if *all* notifications have been unscheduled
1048 prefsPrivate
->scheduled
= FALSE
;
1050 // no need to track changes
1051 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
1053 // clear out any pending notifications
1054 changedKeys
= SCDynamicStoreCopyNotifiedKeys(prefsPrivate
->session
);
1055 if (changedKeys
!= NULL
) {
1056 CFRelease(changedKeys
);
1059 // remove SCDynamicStore session, release 'prefs' reference
1060 __SCPreferencesRemoveSession(prefs
);
1067 pthread_mutex_unlock(&prefsPrivate
->lock
);
1073 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
1074 CFRunLoopRef runLoop
,
1075 CFStringRef runLoopMode
)
1077 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1078 _SCErrorSet(kSCStatusInvalidArgument
);
1082 return __SCPreferencesScheduleWithRunLoop(prefs
, runLoop
, runLoopMode
, NULL
);
1087 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
1088 CFRunLoopRef runLoop
,
1089 CFStringRef runLoopMode
)
1091 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1092 _SCErrorSet(kSCStatusInvalidArgument
);
1096 return __SCPreferencesUnscheduleFromRunLoop(prefs
, runLoop
, runLoopMode
);
1101 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs
,
1102 dispatch_queue_t queue
)
1106 if (!isA_SCPreferences(prefs
)) {
1107 /* sorry, you must provide a session */
1108 _SCErrorSet(kSCStatusNoPrefsSession
);
1112 if (queue
!= NULL
) {
1113 ok
= __SCPreferencesScheduleWithRunLoop(prefs
, NULL
, NULL
, queue
);
1115 ok
= __SCPreferencesUnscheduleFromRunLoop(prefs
, NULL
, NULL
);
1123 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs
)
1126 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1127 uint32_t status
= kSCStatusOK
;
1129 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
1134 // have the helper "synchronize" the prefs
1135 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
1136 SCHELPER_MSG_PREFS_SYNCHRONIZE
,
1142 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
1143 _SCHelperClose(&prefsPrivate
->helper_port
);
1152 SCPreferencesSynchronize(SCPreferencesRef prefs
)
1154 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1156 if (!isA_SCPreferences(prefs
)) {
1157 /* sorry, you must provide a session */
1158 _SCErrorSet(kSCStatusNoPrefsSession
);
1162 if (prefsPrivate
->authorizationData
!= NULL
) {
1163 __SCPreferencesSynchronize_helper(prefs
);
1165 if (prefsPrivate
->prefs
!= NULL
) {
1166 CFRelease(prefsPrivate
->prefs
);
1167 prefsPrivate
->prefs
= NULL
;
1169 if (prefsPrivate
->signature
!= NULL
) {
1170 CFRelease(prefsPrivate
->signature
);
1171 prefsPrivate
->signature
= NULL
;
1173 prefsPrivate
->accessed
= FALSE
;
1174 prefsPrivate
->changed
= FALSE
;