2 * Copyright(c) 2000-2016 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 /* release resources */
113 pthread_mutex_destroy(&prefsPrivate
->lock
);
115 if (prefsPrivate
->name
) CFRelease(prefsPrivate
->name
);
116 if (prefsPrivate
->prefsID
) CFRelease(prefsPrivate
->prefsID
);
117 if (prefsPrivate
->options
) CFRelease(prefsPrivate
->options
);
118 if (prefsPrivate
->path
) CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
119 if (prefsPrivate
->newPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->newPath
);
120 if (prefsPrivate
->lockFD
!= -1) {
121 if (prefsPrivate
->lockPath
!= NULL
) {
122 unlink(prefsPrivate
->lockPath
);
124 close(prefsPrivate
->lockFD
);
126 if (prefsPrivate
->lockPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->lockPath
);
127 if (prefsPrivate
->signature
) CFRelease(prefsPrivate
->signature
);
128 if (prefsPrivate
->sessionNoO_EXLOCK
!= NULL
) {
129 CFRelease(prefsPrivate
->sessionNoO_EXLOCK
);
131 if (prefsPrivate
->sessionKeyLock
) CFRelease(prefsPrivate
->sessionKeyLock
);
132 if (prefsPrivate
->sessionKeyCommit
) CFRelease(prefsPrivate
->sessionKeyCommit
);
133 if (prefsPrivate
->sessionKeyApply
) CFRelease(prefsPrivate
->sessionKeyApply
);
134 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
135 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
137 if (prefsPrivate
->prefs
) CFRelease(prefsPrivate
->prefs
);
138 if (prefsPrivate
->authorizationData
!= NULL
) CFRelease(prefsPrivate
->authorizationData
);
139 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
140 (void) _SCHelperExec(prefsPrivate
->helper_port
,
141 SCHELPER_MSG_PREFS_CLOSE
,
145 _SCHelperClose(&prefsPrivate
->helper_port
);
152 static CFTypeID __kSCPreferencesTypeID
= _kCFRuntimeNotATypeID
;
155 static const CFRuntimeClass __SCPreferencesClass
= {
157 "SCPreferences", // className
160 __SCPreferencesDeallocate
, // dealloc
163 NULL
, // copyFormattingDesc
164 __SCPreferencesCopyDescription
// copyDebugDesc
168 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
171 __SCPreferencesInitialize(void) {
172 /* register with CoreFoundation */
173 __kSCPreferencesTypeID
= _CFRuntimeRegisterClass(&__SCPreferencesClass
);
178 static SCPreferencesPrivateRef
179 __SCPreferencesCreatePrivate(CFAllocatorRef allocator
)
181 SCPreferencesPrivateRef prefsPrivate
;
184 /* initialize runtime */
185 pthread_once(&initialized
, __SCPreferencesInitialize
);
187 /* allocate prefs session */
188 size
= sizeof(SCPreferencesPrivate
) - sizeof(CFRuntimeBase
);
189 prefsPrivate
= (SCPreferencesPrivateRef
)_CFRuntimeCreateInstance(allocator
,
190 __kSCPreferencesTypeID
,
193 if (prefsPrivate
== NULL
) {
197 /* initialize non-zero/NULL members */
198 pthread_mutex_init(&prefsPrivate
->lock
, NULL
);
199 prefsPrivate
->lockFD
= -1;
200 prefsPrivate
->isRoot
= (geteuid() == 0);
206 __private_extern__ Boolean
207 __SCPreferencesCreate_helper(SCPreferencesRef prefs
)
209 CFDataRef data
= NULL
;
210 CFMutableDictionaryRef info
;
213 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
214 uint32_t status
= kSCStatusOK
;
216 uint32_t pid
= getpid();
219 ok
= _SCHelperOpen(prefsPrivate
->authorizationData
,
220 &prefsPrivate
->helper_port
);
225 // create a dictionary of information to pass to the helper
226 info
= CFDictionaryCreateMutable(NULL
,
228 &kCFTypeDictionaryKeyCallBacks
,
229 &kCFTypeDictionaryValueCallBacks
);
232 if (prefsPrivate
->prefsID
!= NULL
) {
233 CFDictionarySetValue(info
, CFSTR("prefsID"), prefsPrivate
->prefsID
);
237 if (prefsPrivate
->options
!= NULL
) {
238 CFDictionarySetValue(info
, CFSTR("options"), prefsPrivate
->options
);
241 // save preferences session "name"
242 CFDictionarySetValue(info
, CFSTR("name"), prefsPrivate
->name
);
245 num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &pid
);
246 CFDictionarySetValue(info
, CFSTR("PID"), num
);
250 str
= CFStringCreateWithCString(NULL
, getprogname(), kCFStringEncodingUTF8
);
251 CFDictionarySetValue(info
, CFSTR("PROC_NAME"), str
);
254 // serialize the info
255 ok
= _SCSerialize(info
, &data
, NULL
, NULL
);
257 if (data
== NULL
|| !ok
) {
261 // have the helper "open" the prefs
262 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
263 SCHELPER_MSG_PREFS_OPEN
,
267 if (data
!= NULL
) CFRelease(data
);
272 if (status
!= kSCStatusOK
) {
281 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
282 _SCHelperClose(&prefsPrivate
->helper_port
);
285 status
= kSCStatusAccessError
;
296 __SCPreferencesAccess_helper(SCPreferencesRef prefs
)
299 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
300 CFDictionaryRef serverDict
= NULL
;
301 CFDictionaryRef serverPrefs
= NULL
;
302 CFDictionaryRef serverSignature
= NULL
;
303 uint32_t status
= kSCStatusOK
;
304 CFDataRef reply
= NULL
;
306 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
307 ok
= __SCPreferencesCreate_helper(prefs
);
313 // have the helper "access" the prefs
314 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
315 SCHELPER_MSG_PREFS_ACCESS
,
323 if (status
!= kSCStatusOK
) {
331 ok
= _SCUnserialize((CFPropertyListRef
*)&serverDict
, reply
, NULL
, 0);
337 if (isA_CFDictionary(serverDict
)) {
338 serverPrefs
= CFDictionaryGetValue(serverDict
, CFSTR("preferences"));
339 serverPrefs
= isA_CFDictionary(serverPrefs
);
341 serverSignature
= CFDictionaryGetValue(serverDict
, CFSTR("signature"));
342 serverSignature
= isA_CFData(serverSignature
);
345 if ((serverPrefs
== NULL
) || (serverSignature
== NULL
)) {
346 if (serverDict
!= NULL
) CFRelease(serverDict
);
350 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, serverPrefs
);
351 prefsPrivate
->signature
= CFRetain(serverSignature
);
352 prefsPrivate
->accessed
= TRUE
;
353 CFRelease(serverDict
);
360 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
361 _SCHelperClose(&prefsPrivate
->helper_port
);
364 status
= kSCStatusAccessError
;
374 static SCPreferencesPrivateRef
375 __SCPreferencesCreate(CFAllocatorRef allocator
,
378 CFDataRef authorizationData
,
379 CFDictionaryRef options
)
381 SCPreferencesPrivateRef prefsPrivate
;
382 int sc_status
= kSCStatusOK
;
385 * allocate and initialize a new prefs session
387 prefsPrivate
= __SCPreferencesCreatePrivate(allocator
);
388 if (prefsPrivate
== NULL
) {
392 prefsPrivate
->name
= CFStringCreateCopy(allocator
, name
);
393 if (prefsID
!= NULL
) {
394 prefsPrivate
->prefsID
= CFStringCreateCopy(allocator
, prefsID
);
396 if (authorizationData
!= NULL
) {
397 prefsPrivate
->authorizationData
= CFRetain(authorizationData
);
399 if (options
!= NULL
) {
400 prefsPrivate
->options
= CFDictionaryCreateCopy(allocator
, options
);
406 * convert prefsID to path
408 prefsPrivate
->path
= __SCPreferencesPath(allocator
,
410 (prefsPrivate
->newPath
== NULL
));
411 if (prefsPrivate
->path
== NULL
) {
412 sc_status
= kSCStatusFailed
;
416 if (access(prefsPrivate
->path
, R_OK
) == 0) {
423 if ((prefsID
== NULL
) || !CFStringHasPrefix(prefsID
, CFSTR("/"))) {
424 /* if default preference ID or relative path */
425 if (prefsPrivate
->newPath
== NULL
) {
427 * we've looked in the "new" prefs directory
428 * without success. Save the "new" path and
429 * look in the "old" prefs directory.
431 prefsPrivate
->newPath
= prefsPrivate
->path
;
435 * we've looked in both the "new" and "old"
436 * prefs directories without success. Use
439 CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
440 prefsPrivate
->path
= prefsPrivate
->newPath
;
441 prefsPrivate
->newPath
= NULL
;
445 /* no preference data, start fresh */
446 sc_status
= kSCStatusNoConfigFile
;
450 if (prefsPrivate
->authorizationData
!= NULL
) {
451 /* no problem, we'll be using the helper */
455 SC_log(LOG_INFO
, "open() failed: %s", strerror(errno
));
456 sc_status
= kSCStatusAccessError
;
459 SC_log(LOG_INFO
, "open() failed: %s", strerror(errno
));
460 sc_status
= kSCStatusFailed
;
466 CFRelease(prefsPrivate
);
467 _SCErrorSet(sc_status
);
473 _SCErrorSet(sc_status
);
478 __private_extern__
void
479 __SCPreferencesAccess(SCPreferencesRef prefs
)
481 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
483 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
486 if (prefsPrivate
->accessed
) {
487 // if preference data has already been accessed
491 if (access(prefsPrivate
->path
, R_OK
) == 0) {
492 fd
= open(prefsPrivate
->path
, O_RDONLY
, 0644);
498 if (fstat(fd
, &statBuf
) == -1) {
499 SC_log(LOG_NOTICE
, "fstat() failed: %s", strerror(errno
));
500 bzero(&statBuf
, sizeof(statBuf
));
505 /* no preference data, start fresh */
509 if (prefsPrivate
->authorizationData
!= NULL
) {
510 if (__SCPreferencesAccess_helper(prefs
)) {
513 SC_log(LOG_NOTICE
, "__SCPreferencesAccess_helper() failed: %s",
514 SCErrorString(SCError()));
520 SC_log(LOG_NOTICE
, "open() failed: %s", strerror(errno
));
523 bzero(&statBuf
, sizeof(statBuf
));
526 if (prefsPrivate
->signature
!= NULL
) CFRelease(prefsPrivate
->signature
);
527 prefsPrivate
->signature
= __SCPSignatureFromStatbuf(&statBuf
);
529 if (statBuf
.st_size
> 0) {
530 CFDictionaryRef dict
;
531 CFErrorRef error
= NULL
;
532 CFMutableDataRef xmlData
;
535 * extract property list
537 xmlData
= CFDataCreateMutable(allocator
, (CFIndex
)statBuf
.st_size
);
538 CFDataSetLength(xmlData
, (CFIndex
)statBuf
.st_size
);
539 if (read(fd
, (void *)CFDataGetBytePtr(xmlData
), (CFIndex
)statBuf
.st_size
) != (CFIndex
)statBuf
.st_size
) {
540 /* corrupt prefs file, start fresh */
541 SC_log(LOG_INFO
, "read(): could not load preference data");
550 dict
= CFPropertyListCreateWithData(allocator
, xmlData
, kCFPropertyListImmutable
, NULL
, &error
);
553 /* corrupt prefs file, start fresh */
555 SC_log(LOG_NOTICE
, "CFPropertyListCreateWithData(): %@", error
);
562 * make sure that we've got a dictionary
564 if (!isA_CFDictionary(dict
)) {
565 /* corrupt prefs file, start fresh */
566 SC_log(LOG_INFO
, "CFGetTypeID(): not a dictionary");
571 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(allocator
, 0, dict
);
581 if (prefsPrivate
->prefs
== NULL
) {
583 * new file, create empty preferences
585 // SC_log(LOG_INFO, "creating new preferences file");
586 prefsPrivate
->prefs
= CFDictionaryCreateMutable(allocator
,
588 &kCFTypeDictionaryKeyCallBacks
,
589 &kCFTypeDictionaryValueCallBacks
);
590 prefsPrivate
->changed
= TRUE
;
593 SC_log(LOG_DEBUG
, "SCPreferences() access: %s",
594 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
596 prefsPrivate
->accessed
= TRUE
;
602 SCPreferencesCreate(CFAllocatorRef allocator
,
606 SCPreferencesPrivateRef prefsPrivate
;
608 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, NULL
, NULL
);
609 return (SCPreferencesRef
)prefsPrivate
;
614 SCPreferencesCreateWithAuthorization(CFAllocatorRef allocator
,
617 AuthorizationRef authorization
)
619 SCPreferencesRef prefs
;
621 #if !TARGET_OS_IPHONE
622 if (authorization
== NULL
) {
623 authorization
= kSCPreferencesUseEntitlementAuthorization
;
625 #else // !TARGET_OS_IPHONE
626 authorization
= kSCPreferencesUseEntitlementAuthorization
;
627 #endif // !TARGET_OS_IPHONE
629 prefs
= SCPreferencesCreateWithOptions(allocator
, name
, prefsID
, authorization
, NULL
);
635 SCPreferencesCreateWithOptions(CFAllocatorRef allocator
,
638 AuthorizationRef authorization
,
639 CFDictionaryRef options
)
641 CFDataRef authorizationData
= NULL
;
642 SCPreferencesPrivateRef prefsPrivate
;
644 if (options
!= NULL
) {
645 if (!isA_CFDictionary(options
)) {
646 _SCErrorSet(kSCStatusInvalidArgument
);
651 if (authorization
!= NULL
) {
652 CFMutableDictionaryRef authorizationDict
;
654 CFStringRef bundleID
= NULL
;
656 authorizationDict
= CFDictionaryCreateMutable(NULL
,
658 &kCFTypeDictionaryKeyCallBacks
,
659 &kCFTypeDictionaryValueCallBacks
);
660 #if !TARGET_OS_IPHONE
661 if (authorization
!= kSCPreferencesUseEntitlementAuthorization
) {
663 AuthorizationExternalForm extForm
;
666 os_status
= AuthorizationMakeExternalForm(authorization
, &extForm
);
667 if (os_status
!= errAuthorizationSuccess
) {
668 SC_log(LOG_INFO
, "AuthorizationMakeExternalForm() failed");
669 _SCErrorSet(kSCStatusInvalidArgument
);
670 CFRelease(authorizationDict
);
674 data
= CFDataCreate(NULL
, (const UInt8
*)extForm
.bytes
, sizeof(extForm
.bytes
));
675 CFDictionaryAddValue(authorizationDict
,
676 kSCHelperAuthAuthorization
,
682 /* get the application/executable/bundle name */
683 bundle
= CFBundleGetMainBundle();
684 if (bundle
!= NULL
) {
685 bundleID
= CFBundleGetIdentifier(bundle
);
686 if (bundleID
!= NULL
) {
691 url
= CFBundleCopyExecutableURL(bundle
);
693 bundleID
= CFURLCopyPath(url
);
698 if (bundleID
!= NULL
) {
699 if (CFEqual(bundleID
, CFSTR("/"))) {
705 if (bundleID
== NULL
) {
706 bundleID
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Unknown(%d)"), getpid());
708 CFDictionaryAddValue(authorizationDict
,
709 kSCHelperAuthCallerInfo
,
713 if (authorizationDict
!= NULL
) {
714 (void) _SCSerialize((CFPropertyListRef
)authorizationDict
,
718 CFRelease(authorizationDict
);
722 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, authorizationData
, options
);
723 if (authorizationData
!= NULL
) CFRelease(authorizationData
);
725 return (SCPreferencesRef
)prefsPrivate
;
730 SCPreferencesGetTypeID(void) {
731 pthread_once(&initialized
, __SCPreferencesInitialize
); /* initialize runtime */
732 return __kSCPreferencesTypeID
;
737 prefsNotify(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
740 void (*context_release
)(const void *);
743 SCPreferencesNotification notify
= 0;
744 SCPreferencesRef prefs
= (SCPreferencesRef
)info
;
745 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
746 SCPreferencesCallBack rlsFunction
;
748 n
= (changedKeys
!= NULL
) ? CFArrayGetCount(changedKeys
) : 0;
749 for (i
= 0; i
< n
; i
++) {
752 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
753 if (CFEqual(key
, prefsPrivate
->sessionKeyCommit
)) {
754 // if preferences have been saved
755 notify
|= kSCPreferencesNotificationCommit
;
757 if (CFEqual(key
, prefsPrivate
->sessionKeyApply
)) {
758 // if stored preferences should be applied to current configuration
759 notify
|= kSCPreferencesNotificationApply
;
768 pthread_mutex_lock(&prefsPrivate
->lock
);
771 rlsFunction
= prefsPrivate
->rlsFunction
;
772 if (prefsPrivate
->rlsContext
.retain
!= NULL
) {
773 context_info
= (void *)prefsPrivate
->rlsContext
.retain(prefsPrivate
->rlsContext
.info
);
774 context_release
= prefsPrivate
->rlsContext
.release
;
776 context_info
= prefsPrivate
->rlsContext
.info
;
777 context_release
= NULL
;
780 pthread_mutex_unlock(&prefsPrivate
->lock
);
782 if (rlsFunction
!= NULL
) {
783 SC_log(LOG_DEBUG
, "exec SCPreferences callout: %s%s%s",
784 ((notify
& kSCPreferencesNotificationCommit
) != 0) ? "commit" : "",
785 ((notify
& kSCPreferencesNotificationCommit
|kSCPreferencesNotificationApply
) != 0) ? ", " : "",
786 ((notify
& kSCPreferencesNotificationApply
) != 0) ? "apply" : "");
787 (*rlsFunction
)(prefs
, notify
, context_info
);
790 if (context_release
!= NULL
) {
791 (*context_release
)(context_info
);
798 __private_extern__
void
799 __SCPreferencesAddSessionKeys(SCPreferencesRef prefs
)
801 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
803 /* create the session "commit" key */
804 if (prefsPrivate
->sessionKeyCommit
== NULL
) {
805 prefsPrivate
->sessionKeyCommit
= _SCPNotificationKey(NULL
,
806 prefsPrivate
->prefsID
,
807 kSCPreferencesKeyCommit
);
810 /* create the session "apply" key */
811 if (prefsPrivate
->sessionKeyApply
== NULL
) {
812 prefsPrivate
->sessionKeyApply
= _SCPNotificationKey(NULL
,
813 prefsPrivate
->prefsID
,
814 kSCPreferencesKeyApply
);
821 __private_extern__ Boolean
822 __SCPreferencesAddSession(SCPreferencesRef prefs
)
824 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
825 SCDynamicStoreContext context
= { 0
831 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
833 if (prefsPrivate
->sessionRefcnt
== 0) {
834 /* establish a dynamic store session */
835 prefsPrivate
->session
= SCDynamicStoreCreate(allocator
,
839 if (prefsPrivate
->session
== NULL
) {
840 SC_log(LOG_INFO
, "SCDynamicStoreCreate() failed");
845 prefsPrivate
->sessionRefcnt
++;
850 __private_extern__
void
851 __SCPreferencesRemoveSession(SCPreferencesRef prefs
)
853 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
855 if (prefsPrivate
->sessionRefcnt
> 0) {
856 if (--prefsPrivate
->sessionRefcnt
== 0) {
857 CFRelease(prefsPrivate
->session
);
858 prefsPrivate
->session
= NULL
;
867 SCPreferencesSetCallback(SCPreferencesRef prefs
,
868 SCPreferencesCallBack callout
,
869 SCPreferencesContext
*context
)
871 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
873 if (!isA_SCPreferences(prefs
)) {
874 /* sorry, you must provide a session */
875 _SCErrorSet(kSCStatusNoPrefsSession
);
879 pthread_mutex_lock(&prefsPrivate
->lock
);
881 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
882 /* let go of the current context */
883 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
886 prefsPrivate
->rlsFunction
= callout
;
887 prefsPrivate
->rlsContext
.info
= NULL
;
888 prefsPrivate
->rlsContext
.retain
= NULL
;
889 prefsPrivate
->rlsContext
.release
= NULL
;
890 prefsPrivate
->rlsContext
.copyDescription
= NULL
;
891 if (context
!= NULL
) {
892 bcopy(context
, &prefsPrivate
->rlsContext
, sizeof(SCPreferencesContext
));
893 if (context
->retain
!= NULL
) {
894 prefsPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
898 pthread_mutex_unlock(&prefsPrivate
->lock
);
905 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
906 CFRunLoopRef runLoop
,
907 CFStringRef runLoopMode
,
908 dispatch_queue_t queue
)
911 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
913 pthread_mutex_lock(&prefsPrivate
->lock
);
915 if ((prefsPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
916 ((queue
!= NULL
) && prefsPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
917 _SCErrorSet(kSCStatusInvalidArgument
);
921 if (!prefsPrivate
->scheduled
) {
922 CFMutableArrayRef keys
;
924 // add SCDynamicStore session (for notifications) ... and hold a 'prefs' reference
925 if (prefsPrivate
->session
== NULL
) {
926 ok
= __SCPreferencesAddSession(prefs
);
930 assert(prefsPrivate
->session
!= NULL
);
933 // add SCDynamicStore "keys"
934 __SCPreferencesAddSessionKeys(prefs
);
936 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
937 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyCommit
);
938 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyApply
);
939 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, keys
, NULL
);
942 if (runLoop
!= NULL
) {
943 prefsPrivate
->rls
= SCDynamicStoreCreateRunLoopSource(NULL
, prefsPrivate
->session
, 0);
944 prefsPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
947 prefsPrivate
->scheduled
= TRUE
;
951 ok
= SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, queue
);
953 prefsPrivate
->scheduled
= FALSE
;
954 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
955 __SCPreferencesRemoveSession(prefs
);
959 prefsPrivate
->dispatchQueue
= queue
;
960 dispatch_retain(prefsPrivate
->dispatchQueue
);
962 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
964 * if we do not already have notifications scheduled with
965 * this runLoop / runLoopMode
967 CFRunLoopAddSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
970 _SC_schedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
);
977 pthread_mutex_unlock(&prefsPrivate
->lock
);
983 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
984 CFRunLoopRef runLoop
,
985 CFStringRef runLoopMode
)
987 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
991 pthread_mutex_lock(&prefsPrivate
->lock
);
993 if ((runLoop
!= NULL
) && !prefsPrivate
->scheduled
) { // if we should be scheduled (but are not)
994 _SCErrorSet(kSCStatusInvalidArgument
);
998 if (((runLoop
== NULL
) && (prefsPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
999 ((runLoop
!= NULL
) && (prefsPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1000 _SCErrorSet(kSCStatusInvalidArgument
);
1004 if (runLoop
== NULL
) {
1005 SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, NULL
);
1006 dispatch_release(prefsPrivate
->dispatchQueue
);
1007 prefsPrivate
->dispatchQueue
= NULL
;
1009 if (!_SC_unschedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
, FALSE
)) {
1010 // if not currently scheduled on this runLoop / runLoopMode
1011 _SCErrorSet(kSCStatusInvalidArgument
);
1015 n
= CFArrayGetCount(prefsPrivate
->rlList
);
1016 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
1018 * if we are no longer scheduled to receive notifications for
1019 * this runLoop / runLoopMode
1021 CFRunLoopRemoveSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
1024 // if *all* notifications have been unscheduled
1025 CFRelease(prefsPrivate
->rlList
);
1026 prefsPrivate
->rlList
= NULL
;
1027 CFRunLoopSourceInvalidate(prefsPrivate
->rls
);
1028 CFRelease(prefsPrivate
->rls
);
1029 prefsPrivate
->rls
= NULL
;
1035 CFArrayRef changedKeys
;
1037 // if *all* notifications have been unscheduled
1038 prefsPrivate
->scheduled
= FALSE
;
1040 // no need to track changes
1041 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
1043 // clear out any pending notifications
1044 changedKeys
= SCDynamicStoreCopyNotifiedKeys(prefsPrivate
->session
);
1045 if (changedKeys
!= NULL
) {
1046 CFRelease(changedKeys
);
1049 // remove SCDynamicStore session, release 'prefs' reference
1050 __SCPreferencesRemoveSession(prefs
);
1057 pthread_mutex_unlock(&prefsPrivate
->lock
);
1063 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
1064 CFRunLoopRef runLoop
,
1065 CFStringRef runLoopMode
)
1067 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1068 _SCErrorSet(kSCStatusInvalidArgument
);
1072 return __SCPreferencesScheduleWithRunLoop(prefs
, runLoop
, runLoopMode
, NULL
);
1077 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
1078 CFRunLoopRef runLoop
,
1079 CFStringRef runLoopMode
)
1081 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1082 _SCErrorSet(kSCStatusInvalidArgument
);
1086 return __SCPreferencesUnscheduleFromRunLoop(prefs
, runLoop
, runLoopMode
);
1091 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs
,
1092 dispatch_queue_t queue
)
1096 if (!isA_SCPreferences(prefs
)) {
1097 /* sorry, you must provide a session */
1098 _SCErrorSet(kSCStatusNoPrefsSession
);
1102 if (queue
!= NULL
) {
1103 ok
= __SCPreferencesScheduleWithRunLoop(prefs
, NULL
, NULL
, queue
);
1105 ok
= __SCPreferencesUnscheduleFromRunLoop(prefs
, NULL
, NULL
);
1113 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs
)
1116 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1117 uint32_t status
= kSCStatusOK
;
1119 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
1124 // have the helper "synchronize" the prefs
1125 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
1126 SCHELPER_MSG_PREFS_SYNCHRONIZE
,
1132 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
1133 _SCHelperClose(&prefsPrivate
->helper_port
);
1142 SCPreferencesSynchronize(SCPreferencesRef prefs
)
1144 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1146 if (!isA_SCPreferences(prefs
)) {
1147 /* sorry, you must provide a session */
1148 _SCErrorSet(kSCStatusNoPrefsSession
);
1152 SC_log(LOG_DEBUG
, "SCPreferences() synchronize: %s",
1153 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
1155 if (prefsPrivate
->authorizationData
!= NULL
) {
1156 __SCPreferencesSynchronize_helper(prefs
);
1158 if (prefsPrivate
->prefs
!= NULL
) {
1159 CFRelease(prefsPrivate
->prefs
);
1160 prefsPrivate
->prefs
= NULL
;
1162 if (prefsPrivate
->signature
!= NULL
) {
1163 CFRelease(prefsPrivate
->signature
);
1164 prefsPrivate
->signature
= NULL
;
1166 prefsPrivate
->accessed
= FALSE
;
1167 prefsPrivate
->changed
= FALSE
;