2 * Copyright(c) 2000-2009 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>
41 #include <dispatch/dispatch.h>
42 #endif // !TARGET_OS_IPHONE
43 #include <SystemConfiguration/SystemConfiguration.h>
44 #include <SystemConfiguration/SCValidation.h>
45 #include <SystemConfiguration/SCPrivate.h>
46 #include "SCPreferencesInternal.h"
47 #include "SCHelper_client.h"
49 #include "dy_framework.h"
54 #include <sys/errno.h>
57 static __inline__ CFTypeRef
58 isA_SCPreferences(CFTypeRef obj
)
60 return (isA_CFType(obj
, SCPreferencesGetTypeID()));
65 __SCPreferencesCopyDescription(CFTypeRef cf
) {
66 CFAllocatorRef allocator
= CFGetAllocator(cf
);
67 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)cf
;
68 CFMutableStringRef result
;
70 result
= CFStringCreateMutable(allocator
, 0);
71 CFStringAppendFormat(result
, NULL
, CFSTR("<SCPreferences %p [%p]> {"), cf
, allocator
);
72 CFStringAppendFormat(result
, NULL
, CFSTR("name = %@"), prefsPrivate
->name
);
73 CFStringAppendFormat(result
, NULL
, CFSTR(", id = %@"), prefsPrivate
->prefsID
);
74 CFStringAppendFormat(result
, NULL
, CFSTR(", path = %s"),
75 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
76 if (prefsPrivate
->accessed
) {
77 CFStringAppendFormat(result
, NULL
, CFSTR(", accessed"));
79 if (prefsPrivate
->changed
) {
80 CFStringAppendFormat(result
, NULL
, CFSTR(", changed"));
82 if (prefsPrivate
->locked
) {
83 CFStringAppendFormat(result
, NULL
, CFSTR(", locked"));
85 if (prefsPrivate
->helper
!= -1) {
86 CFStringAppendFormat(result
, NULL
, CFSTR(", helper fd=%d"), prefsPrivate
->helper
);
88 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
95 __SCPreferencesDeallocate(CFTypeRef cf
)
97 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)cf
;
99 /* release resources */
101 pthread_mutex_destroy(&prefsPrivate
->lock
);
103 if (prefsPrivate
->name
) CFRelease(prefsPrivate
->name
);
104 if (prefsPrivate
->prefsID
) CFRelease(prefsPrivate
->prefsID
);
105 if (prefsPrivate
->path
) CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
106 if (prefsPrivate
->newPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->newPath
);
107 if (prefsPrivate
->lockFD
!= -1) {
108 if (prefsPrivate
->lockPath
!= NULL
) {
109 unlink(prefsPrivate
->lockPath
);
111 close(prefsPrivate
->lockFD
);
113 if (prefsPrivate
->lockPath
) CFAllocatorDeallocate(NULL
, prefsPrivate
->lockPath
);
114 if (prefsPrivate
->signature
) CFRelease(prefsPrivate
->signature
);
115 if (prefsPrivate
->session
) CFRelease(prefsPrivate
->session
);
116 if (prefsPrivate
->sessionKeyCommit
) CFRelease(prefsPrivate
->sessionKeyCommit
);
117 if (prefsPrivate
->sessionKeyApply
) CFRelease(prefsPrivate
->sessionKeyApply
);
118 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
119 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
121 if (prefsPrivate
->prefs
) CFRelease(prefsPrivate
->prefs
);
122 if (prefsPrivate
->authorizationData
!= NULL
) CFRelease(prefsPrivate
->authorizationData
);
123 if (prefsPrivate
->helper
!= -1) {
124 (void) _SCHelperExec(prefsPrivate
->helper
, SCHELPER_MSG_PREFS_CLOSE
, NULL
, NULL
, NULL
);
125 _SCHelperClose(prefsPrivate
->helper
);
132 static CFTypeID __kSCPreferencesTypeID
= _kCFRuntimeNotATypeID
;
135 static const CFRuntimeClass __SCPreferencesClass
= {
137 "SCPreferences", // className
140 __SCPreferencesDeallocate
, // dealloc
143 NULL
, // copyFormattingDesc
144 __SCPreferencesCopyDescription
// copyDebugDesc
148 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
151 __SCPreferencesInitialize(void) {
152 __kSCPreferencesTypeID
= _CFRuntimeRegisterClass(&__SCPreferencesClass
);
157 static SCPreferencesPrivateRef
158 __SCPreferencesCreatePrivate(CFAllocatorRef allocator
)
160 SCPreferencesPrivateRef prefsPrivate
;
163 /* initialize runtime */
164 pthread_once(&initialized
, __SCPreferencesInitialize
);
166 /* allocate prefs session */
167 size
= sizeof(SCPreferencesPrivate
) - sizeof(CFRuntimeBase
);
168 prefsPrivate
= (SCPreferencesPrivateRef
)_CFRuntimeCreateInstance(allocator
,
169 __kSCPreferencesTypeID
,
172 if (prefsPrivate
== NULL
) {
176 pthread_mutex_init(&prefsPrivate
->lock
, NULL
);
178 prefsPrivate
->name
= NULL
;
179 prefsPrivate
->prefsID
= NULL
;
180 prefsPrivate
->path
= NULL
;
181 prefsPrivate
->newPath
= NULL
; // new prefs path
182 prefsPrivate
->locked
= FALSE
;
183 prefsPrivate
->lockFD
= -1;
184 prefsPrivate
->lockPath
= NULL
;
185 prefsPrivate
->signature
= NULL
;
186 prefsPrivate
->session
= NULL
;
187 prefsPrivate
->sessionKeyCommit
= NULL
;
188 prefsPrivate
->sessionKeyApply
= NULL
;
189 prefsPrivate
->scheduled
= FALSE
;
190 prefsPrivate
->rls
= NULL
;
191 prefsPrivate
->rlsFunction
= NULL
;
192 prefsPrivate
->rlsContext
.info
= NULL
;
193 prefsPrivate
->rlsContext
.retain
= NULL
;
194 prefsPrivate
->rlsContext
.release
= NULL
;
195 prefsPrivate
->rlsContext
.copyDescription
= NULL
;
196 prefsPrivate
->rlList
= NULL
;
197 #if !TARGET_OS_IPHONE
198 prefsPrivate
->dispatchQueue
= NULL
;
199 #endif // !TARGET_OS_IPHONE
200 prefsPrivate
->prefs
= NULL
;
201 prefsPrivate
->accessed
= FALSE
;
202 prefsPrivate
->changed
= FALSE
;
203 prefsPrivate
->isRoot
= (geteuid() == 0);
204 prefsPrivate
->authorizationData
= NULL
;
205 prefsPrivate
->helper
= -1;
211 __private_extern__ Boolean
212 __SCPreferencesCreate_helper(SCPreferencesRef prefs
)
214 CFDataRef data
= NULL
;
215 CFMutableDictionaryRef info
;
218 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
219 uint32_t status
= kSCStatusOK
;
220 uint32_t pid
= getpid();
223 prefsPrivate
->helper
= _SCHelperOpen(prefsPrivate
->authorizationData
);
224 if (prefsPrivate
->helper
== -1) {
228 // create a dictionary of information to pass to the helper
229 info
= CFDictionaryCreateMutable(NULL
,
231 &kCFTypeDictionaryKeyCallBacks
,
232 &kCFTypeDictionaryValueCallBacks
);
233 if (prefsPrivate
->prefsID
!= NULL
) {
234 CFDictionarySetValue(info
, CFSTR("prefsID"), prefsPrivate
->prefsID
);
236 CFDictionarySetValue(info
, CFSTR("name"), prefsPrivate
->name
);
237 num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &pid
);
238 CFDictionarySetValue(info
, CFSTR("PID"), num
);
241 // serialize the info
242 ok
= _SCSerialize(info
, &data
, NULL
, NULL
);
244 if (data
== NULL
|| !ok
) {
248 // have the helper "open" the prefs
249 ok
= _SCHelperExec(prefsPrivate
->helper
,
250 SCHELPER_MSG_PREFS_OPEN
,
254 if (data
!= NULL
) CFRelease(data
);
259 if (status
!= kSCStatusOK
) {
268 if (prefsPrivate
->helper
!= -1) {
269 _SCHelperClose(prefsPrivate
->helper
);
270 prefsPrivate
->helper
= -1;
273 status
= kSCStatusAccessError
;
284 __SCPreferencesAccess_helper(SCPreferencesRef prefs
)
287 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
288 CFDictionaryRef serverDict
= NULL
;
289 CFDictionaryRef serverPrefs
= NULL
;
290 CFDictionaryRef serverSignature
= NULL
;
291 uint32_t status
= kSCStatusOK
;
292 CFDataRef reply
= NULL
;
294 if (prefsPrivate
->helper
== -1) {
295 ok
= __SCPreferencesCreate_helper(prefs
);
301 // have the helper "access" the prefs
302 ok
= _SCHelperExec(prefsPrivate
->helper
,
303 SCHELPER_MSG_PREFS_ACCESS
,
311 if (status
!= kSCStatusOK
) {
319 ok
= _SCUnserialize((CFPropertyListRef
*)&serverDict
, reply
, NULL
, 0);
325 if (isA_CFDictionary(serverDict
)) {
326 serverPrefs
= CFDictionaryGetValue(serverDict
, CFSTR("preferences"));
327 serverSignature
= CFDictionaryGetValue(serverDict
, CFSTR("signature"));
330 if (!isA_CFDictionary(serverPrefs
) || !isA_CFData(serverSignature
)) {
331 CFRelease(serverDict
);
335 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, serverPrefs
);
336 prefsPrivate
->signature
= CFRetain(serverSignature
);
337 prefsPrivate
->accessed
= TRUE
;
338 CFRelease(serverDict
);
345 if (prefsPrivate
->helper
!= -1) {
346 _SCHelperClose(prefsPrivate
->helper
);
347 prefsPrivate
->helper
= -1;
350 status
= kSCStatusAccessError
;
360 static SCPreferencesPrivateRef
361 __SCPreferencesCreate(CFAllocatorRef allocator
,
364 CFDataRef authorizationData
)
367 SCPreferencesPrivateRef prefsPrivate
;
368 int sc_status
= kSCStatusOK
;
371 * allocate and initialize a new prefs session
373 prefsPrivate
= __SCPreferencesCreatePrivate(allocator
);
374 if (prefsPrivate
== NULL
) {
378 prefsPrivate
->name
= CFStringCreateCopy(allocator
, name
);
379 if (prefsID
!= NULL
) {
380 prefsPrivate
->prefsID
= CFStringCreateCopy(allocator
, prefsID
);
382 if (authorizationData
!= NULL
) {
383 prefsPrivate
->authorizationData
= CFRetain(authorizationData
);
389 * convert prefsID to path
391 prefsPrivate
->path
= __SCPreferencesPath(allocator
,
393 (prefsPrivate
->newPath
== NULL
));
394 if (prefsPrivate
->path
== NULL
) {
395 sc_status
= kSCStatusFailed
;
402 fd
= open(prefsPrivate
->path
, O_RDONLY
, 0644);
409 if ((prefsID
== NULL
) || !CFStringHasPrefix(prefsID
, CFSTR("/"))) {
410 /* if default preference ID or relative path */
411 if (prefsPrivate
->newPath
== NULL
) {
413 * we've looked in the "new" prefs directory
414 * without success. Save the "new" path and
415 * look in the "old" prefs directory.
417 prefsPrivate
->newPath
= prefsPrivate
->path
;
421 * we've looked in both the "new" and "old"
422 * prefs directories without success. Use
425 CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
426 prefsPrivate
->path
= prefsPrivate
->newPath
;
427 prefsPrivate
->newPath
= NULL
;
431 /* no preference data, start fresh */
432 sc_status
= kSCStatusNoConfigFile
;
435 if (prefsPrivate
->authorizationData
!= NULL
) {
436 /* no problem, we'll be using the helper */
440 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCPreferencesCreate open() failed: %s"), strerror(errno
));
441 sc_status
= kSCStatusAccessError
;
444 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCPreferencesCreate open() failed: %s"), strerror(errno
));
445 sc_status
= kSCStatusFailed
;
454 _SCErrorSet(sc_status
);
459 if (fd
!= -1) (void) close(fd
);
460 CFRelease(prefsPrivate
);
461 _SCErrorSet(sc_status
);
466 __private_extern__
void
467 __SCPreferencesAccess(SCPreferencesRef prefs
)
469 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
471 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
474 if (prefsPrivate
->accessed
) {
475 // if preference data has already been accessed
479 fd
= open(prefsPrivate
->path
, O_RDONLY
, 0644);
482 if (fstat(fd
, &statBuf
) == -1) {
483 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCPreferencesAccess fstat() failed: %s"), strerror(errno
));
484 bzero(&statBuf
, sizeof(statBuf
));
489 /* no preference data, start fresh */
492 if (prefsPrivate
->authorizationData
!= NULL
) {
493 if (__SCPreferencesAccess_helper(prefs
)) {
497 CFSTR("__SCPreferencesAccess_helper() failed: %s"),
498 SCErrorString(SCError()));
504 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCPreferencesAccess open() failed: %s"), strerror(errno
));
507 bzero(&statBuf
, sizeof(statBuf
));
510 if (prefsPrivate
->signature
!= NULL
) CFRelease(prefsPrivate
->signature
);
511 prefsPrivate
->signature
= __SCPSignatureFromStatbuf(&statBuf
);
513 if (statBuf
.st_size
> 0) {
514 CFDictionaryRef dict
;
515 CFMutableDataRef xmlData
;
516 CFStringRef xmlError
;
519 * extract property list
521 xmlData
= CFDataCreateMutable(allocator
, statBuf
.st_size
);
522 CFDataSetLength(xmlData
, statBuf
.st_size
);
523 if (read(fd
, (void *)CFDataGetBytePtr(xmlData
), statBuf
.st_size
) != statBuf
.st_size
) {
524 /* corrupt prefs file, start fresh */
525 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCPreferencesAccess read(): could not load preference data."));
534 dict
= CFPropertyListCreateFromXMLData(allocator
,
536 kCFPropertyListImmutable
,
540 /* corrupt prefs file, start fresh */
541 if (xmlError
!= NULL
) {
543 CFSTR("__SCPreferencesAccess CFPropertyListCreateFromXMLData(): %@"),
551 * make sure that we've got a dictionary
553 if (!isA_CFDictionary(dict
)) {
554 /* corrupt prefs file, start fresh */
555 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCPreferencesAccess CFGetTypeID(): not a dictionary."));
560 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(allocator
, 0, dict
);
570 if (prefsPrivate
->prefs
== NULL
) {
572 * new file, create empty preferences
574 // SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess(): creating new preferences file."));
575 prefsPrivate
->prefs
= CFDictionaryCreateMutable(allocator
,
577 &kCFTypeDictionaryKeyCallBacks
,
578 &kCFTypeDictionaryValueCallBacks
);
579 prefsPrivate
->changed
= TRUE
;
582 prefsPrivate
->accessed
= TRUE
;
588 SCPreferencesCreate(CFAllocatorRef allocator
,
592 SCPreferencesPrivateRef prefsPrivate
;
594 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, NULL
);
595 return (SCPreferencesRef
)prefsPrivate
;
600 SCPreferencesCreateWithAuthorization(CFAllocatorRef allocator
,
603 AuthorizationRef authorization
)
605 CFDataRef authorizationData
= NULL
;
606 SCPreferencesPrivateRef prefsPrivate
;
608 #if !TARGET_OS_IPHONE
609 AuthorizationExternalForm extForm
;
612 os_status
= AuthorizationMakeExternalForm(authorization
, &extForm
);
613 if (os_status
!= errAuthorizationSuccess
) {
614 SCLog(TRUE
, LOG_INFO
, CFSTR("_SCHelperOpen AuthorizationMakeExternalForm() failed"));
615 _SCErrorSet(kSCStatusInvalidArgument
);
619 authorizationData
= CFDataCreate(NULL
, (const UInt8
*)extForm
.bytes
, sizeof(extForm
.bytes
));
620 #else // !TARGET_OS_IPHONE
622 CFStringRef bundleID
= NULL
;
624 /* get the application/executable/bundle name */
625 bundle
= CFBundleGetMainBundle();
626 if (bundle
!= NULL
) {
627 bundleID
= CFBundleGetIdentifier(bundle
);
628 if (bundleID
!= NULL
) {
633 url
= CFBundleCopyExecutableURL(bundle
);
635 bundleID
= CFURLCopyPath(url
);
640 if (bundleID
!= NULL
) {
641 if (CFEqual(bundleID
, CFSTR("/"))) {
647 if (bundleID
== NULL
) {
648 bundleID
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Unknown(%d)"), getpid());
651 _SCSerializeString(bundleID
, &authorizationData
, NULL
, NULL
);
653 #endif // !TARGET_OS_IPHONE
655 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, authorizationData
);
656 CFRelease(authorizationData
);
658 return (SCPreferencesRef
)prefsPrivate
;
663 SCPreferencesGetTypeID(void) {
664 pthread_once(&initialized
, __SCPreferencesInitialize
); /* initialize runtime */
665 return __kSCPreferencesTypeID
;
670 prefsNotify(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
673 void (*context_release
)(const void *);
676 SCPreferencesNotification notify
= 0;
677 SCPreferencesRef prefs
= (SCPreferencesRef
)info
;
678 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
679 SCPreferencesCallBack rlsFunction
;
681 n
= (changedKeys
!= NULL
) ? CFArrayGetCount(changedKeys
) : 0;
682 for (i
= 0; i
< n
; i
++) {
685 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
686 if (CFEqual(key
, prefsPrivate
->sessionKeyCommit
)) {
687 // if preferences have been saved
688 notify
|= kSCPreferencesNotificationCommit
;
690 if (CFEqual(key
, prefsPrivate
->sessionKeyApply
)) {
691 // if stored preferences should be applied to current configuration
692 notify
|= kSCPreferencesNotificationApply
;
701 pthread_mutex_lock(&prefsPrivate
->lock
);
704 rlsFunction
= prefsPrivate
->rlsFunction
;
705 if (prefsPrivate
->rlsContext
.retain
!= NULL
) {
706 context_info
= (void *)prefsPrivate
->rlsContext
.retain(prefsPrivate
->rlsContext
.info
);
707 context_release
= prefsPrivate
->rlsContext
.release
;
709 context_info
= prefsPrivate
->rlsContext
.info
;
710 context_release
= NULL
;
713 pthread_mutex_unlock(&prefsPrivate
->lock
);
715 if (rlsFunction
!= NULL
) {
716 (*rlsFunction
)(prefs
, notify
, context_info
);
719 if (context_release
!= NULL
) {
720 (*context_release
)(context_info
);
727 __private_extern__ Boolean
728 __SCPreferencesAddSession(SCPreferencesRef prefs
)
730 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
731 SCDynamicStoreContext context
= { 0
737 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
739 /* establish a dynamic store session */
740 prefsPrivate
->session
= SCDynamicStoreCreate(allocator
,
744 if (prefsPrivate
->session
== NULL
) {
745 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("__SCPreferencesAddSession SCDynamicStoreCreate() failed"));
749 /* create the session "commit" key */
750 prefsPrivate
->sessionKeyCommit
= _SCPNotificationKey(NULL
,
751 prefsPrivate
->prefsID
,
752 kSCPreferencesKeyCommit
);
754 /* create the session "apply" key */
755 prefsPrivate
->sessionKeyApply
= _SCPNotificationKey(NULL
,
756 prefsPrivate
->prefsID
,
757 kSCPreferencesKeyApply
);
764 SCPreferencesSetCallback(SCPreferencesRef prefs
,
765 SCPreferencesCallBack callout
,
766 SCPreferencesContext
*context
)
768 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
770 if (!isA_SCPreferences(prefs
)) {
771 /* sorry, you must provide a session */
772 _SCErrorSet(kSCStatusNoPrefsSession
);
776 pthread_mutex_lock(&prefsPrivate
->lock
);
778 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
779 /* let go of the current context */
780 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
783 prefsPrivate
->rlsFunction
= callout
;
784 prefsPrivate
->rlsContext
.info
= NULL
;
785 prefsPrivate
->rlsContext
.retain
= NULL
;
786 prefsPrivate
->rlsContext
.release
= NULL
;
787 prefsPrivate
->rlsContext
.copyDescription
= NULL
;
788 if (context
!= NULL
) {
789 bcopy(context
, &prefsPrivate
->rlsContext
, sizeof(SCPreferencesContext
));
790 if (context
->retain
!= NULL
) {
791 prefsPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
795 pthread_mutex_unlock(&prefsPrivate
->lock
);
802 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
803 CFRunLoopRef runLoop
,
804 CFStringRef runLoopMode
,
805 #if !TARGET_OS_IPHONE
806 dispatch_queue_t queue
807 #else // !TARGET_OS_IPHONE
809 #endif // !TARGET_OS_IPHONE
813 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
815 pthread_mutex_lock(&prefsPrivate
->lock
);
817 #if !TARGET_OS_IPHONE
818 if ((prefsPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
819 ((queue
!= NULL
) && prefsPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
820 _SCErrorSet(kSCStatusInvalidArgument
);
823 #endif // !TARGET_OS_IPHONE
825 if (!prefsPrivate
->scheduled
) {
826 CFMutableArrayRef keys
;
828 if (prefsPrivate
->session
== NULL
) {
829 ok
= __SCPreferencesAddSession(prefs
);
835 CFRetain(prefs
); // hold a reference to the prefs
837 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
838 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyCommit
);
839 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyApply
);
840 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, keys
, NULL
);
843 if (runLoop
!= NULL
) {
844 prefsPrivate
->rls
= SCDynamicStoreCreateRunLoopSource(NULL
, prefsPrivate
->session
, 0);
845 prefsPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
848 prefsPrivate
->scheduled
= TRUE
;
851 #if !TARGET_OS_IPHONE
853 ok
= SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, queue
);
855 prefsPrivate
->scheduled
= FALSE
;
856 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
861 prefsPrivate
->dispatchQueue
= queue
;
862 dispatch_retain(prefsPrivate
->dispatchQueue
);
864 #endif // !TARGET_OS_IPHONE
866 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
868 * if we do not already have notifications scheduled with
869 * this runLoop / runLoopMode
871 CFRunLoopAddSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
874 _SC_schedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
);
881 pthread_mutex_unlock(&prefsPrivate
->lock
);
887 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
888 CFRunLoopRef runLoop
,
889 CFStringRef runLoopMode
)
891 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
895 pthread_mutex_lock(&prefsPrivate
->lock
);
897 if ((runLoop
!= NULL
) && !prefsPrivate
->scheduled
) { // if we should be scheduled (but are not)
898 _SCErrorSet(kSCStatusInvalidArgument
);
902 #if !TARGET_OS_IPHONE
903 if (((runLoop
== NULL
) && (prefsPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
904 ((runLoop
!= NULL
) && (prefsPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
905 _SCErrorSet(kSCStatusInvalidArgument
);
908 #endif // !TARGET_OS_IPHONE
910 #if !TARGET_OS_IPHONE
911 if (runLoop
== NULL
) {
912 SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, NULL
);
913 dispatch_release(prefsPrivate
->dispatchQueue
);
914 prefsPrivate
->dispatchQueue
= NULL
;
916 #endif // !TARGET_OS_IPHONE
918 if (!_SC_unschedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
, FALSE
)) {
919 // if not currently scheduled on this runLoop / runLoopMode
920 _SCErrorSet(kSCStatusInvalidArgument
);
924 n
= CFArrayGetCount(prefsPrivate
->rlList
);
925 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
927 * if we are no longer scheduled to receive notifications for
928 * this runLoop / runLoopMode
930 CFRunLoopRemoveSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
933 // if *all* notifications have been unscheduled
934 CFRelease(prefsPrivate
->rlList
);
935 prefsPrivate
->rlList
= NULL
;
936 CFRunLoopSourceInvalidate(prefsPrivate
->rls
);
937 CFRelease(prefsPrivate
->rls
);
938 prefsPrivate
->rls
= NULL
;
944 CFArrayRef changedKeys
;
946 // if *all* notifications have been unscheduled
947 prefsPrivate
->scheduled
= FALSE
;
949 // no need to track changes
950 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
952 // clear out any pending notifications
953 changedKeys
= SCDynamicStoreCopyNotifiedKeys(prefsPrivate
->session
);
954 if (changedKeys
!= NULL
) {
955 CFRelease(changedKeys
);
958 // release our reference to the prefs
966 pthread_mutex_unlock(&prefsPrivate
->lock
);
971 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
972 CFRunLoopRef runLoop
,
973 CFStringRef runLoopMode
)
975 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
976 _SCErrorSet(kSCStatusInvalidArgument
);
980 return __SCPreferencesScheduleWithRunLoop(prefs
, runLoop
, runLoopMode
, NULL
);
984 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
985 CFRunLoopRef runLoop
,
986 CFStringRef runLoopMode
)
988 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
989 _SCErrorSet(kSCStatusInvalidArgument
);
993 return __SCPreferencesUnscheduleFromRunLoop(prefs
, runLoop
, runLoopMode
);
996 #if !TARGET_OS_IPHONE
998 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs
,
999 dispatch_queue_t queue
)
1003 if (!isA_SCPreferences(prefs
)) {
1004 /* sorry, you must provide a session */
1005 _SCErrorSet(kSCStatusNoPrefsSession
);
1009 if (queue
!= NULL
) {
1010 ok
= __SCPreferencesScheduleWithRunLoop(prefs
, NULL
, NULL
, queue
);
1012 ok
= __SCPreferencesUnscheduleFromRunLoop(prefs
, NULL
, NULL
);
1017 #endif // !TARGET_OS_IPHONE
1021 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs
)
1024 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1025 uint32_t status
= kSCStatusOK
;
1027 if (prefsPrivate
->helper
== -1) {
1032 // have the helper "synchronize" the prefs
1033 ok
= _SCHelperExec(prefsPrivate
->helper
,
1034 SCHELPER_MSG_PREFS_SYNCHRONIZE
,
1040 if (prefsPrivate
->helper
!= -1) {
1041 _SCHelperClose(prefsPrivate
->helper
);
1042 prefsPrivate
->helper
= -1;
1051 SCPreferencesSynchronize(SCPreferencesRef prefs
)
1053 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1055 if (!isA_SCPreferences(prefs
)) {
1056 /* sorry, you must provide a session */
1057 _SCErrorSet(kSCStatusNoPrefsSession
);
1061 if (prefsPrivate
->authorizationData
!= NULL
) {
1062 __SCPreferencesSynchronize_helper(prefs
);
1064 if (prefsPrivate
->prefs
!= NULL
) {
1065 CFRelease(prefsPrivate
->prefs
);
1066 prefsPrivate
->prefs
= NULL
;
1068 if (prefsPrivate
->signature
!= NULL
) {
1069 CFRelease(prefsPrivate
->signature
);
1070 prefsPrivate
->signature
= NULL
;
1072 prefsPrivate
->accessed
= FALSE
;
1073 prefsPrivate
->changed
= FALSE
;