2 * Copyright(c) 2000-2011 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 static __inline__ CFTypeRef
57 isA_SCPreferences(CFTypeRef obj
)
59 return (isA_CFType(obj
, SCPreferencesGetTypeID()));
64 __SCPreferencesCopyDescription(CFTypeRef cf
) {
65 CFAllocatorRef allocator
= CFGetAllocator(cf
);
66 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)cf
;
67 CFMutableStringRef result
;
69 result
= CFStringCreateMutable(allocator
, 0);
70 CFStringAppendFormat(result
, NULL
, CFSTR("<SCPreferences %p [%p]> {"), cf
, allocator
);
71 CFStringAppendFormat(result
, NULL
, CFSTR("name = %@"), prefsPrivate
->name
);
72 CFStringAppendFormat(result
, NULL
, CFSTR(", id = %@"), prefsPrivate
->prefsID
);
73 CFStringAppendFormat(result
, NULL
, CFSTR(", path = %s"),
74 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
75 if (prefsPrivate
->accessed
) {
76 CFStringAppendFormat(result
, NULL
, CFSTR(", accessed"));
78 if (prefsPrivate
->changed
) {
79 CFStringAppendFormat(result
, NULL
, CFSTR(", changed"));
81 if (prefsPrivate
->locked
) {
82 CFStringAppendFormat(result
, NULL
, CFSTR(", locked"));
84 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
85 CFStringAppendFormat(result
, NULL
, CFSTR(", helper port = %p"), prefsPrivate
->helper_port
);
87 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
94 __SCPreferencesDeallocate(CFTypeRef cf
)
96 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)cf
;
98 /* release resources */
100 pthread_mutex_destroy(&prefsPrivate
->lock
);
102 if (prefsPrivate
->name
) CFRelease(prefsPrivate
->name
);
103 if (prefsPrivate
->prefsID
) CFRelease(prefsPrivate
->prefsID
);
104 if (prefsPrivate
->options
) CFRelease(prefsPrivate
->options
);
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
->sessionKeyLock
) CFRelease(prefsPrivate
->sessionKeyLock
);
117 if (prefsPrivate
->sessionKeyCommit
) CFRelease(prefsPrivate
->sessionKeyCommit
);
118 if (prefsPrivate
->sessionKeyApply
) CFRelease(prefsPrivate
->sessionKeyApply
);
119 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
120 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
122 if (prefsPrivate
->prefs
) CFRelease(prefsPrivate
->prefs
);
123 if (prefsPrivate
->authorizationData
!= NULL
) CFRelease(prefsPrivate
->authorizationData
);
124 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
125 (void) _SCHelperExec(prefsPrivate
->helper_port
,
126 SCHELPER_MSG_PREFS_CLOSE
,
130 _SCHelperClose(&prefsPrivate
->helper_port
);
137 static CFTypeID __kSCPreferencesTypeID
= _kCFRuntimeNotATypeID
;
140 static const CFRuntimeClass __SCPreferencesClass
= {
142 "SCPreferences", // className
145 __SCPreferencesDeallocate
, // dealloc
148 NULL
, // copyFormattingDesc
149 __SCPreferencesCopyDescription
// copyDebugDesc
153 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
156 __SCPreferencesInitialize(void) {
157 __kSCPreferencesTypeID
= _CFRuntimeRegisterClass(&__SCPreferencesClass
);
162 static SCPreferencesPrivateRef
163 __SCPreferencesCreatePrivate(CFAllocatorRef allocator
)
165 SCPreferencesPrivateRef prefsPrivate
;
168 /* initialize runtime */
169 pthread_once(&initialized
, __SCPreferencesInitialize
);
171 /* allocate prefs session */
172 size
= sizeof(SCPreferencesPrivate
) - sizeof(CFRuntimeBase
);
173 prefsPrivate
= (SCPreferencesPrivateRef
)_CFRuntimeCreateInstance(allocator
,
174 __kSCPreferencesTypeID
,
177 if (prefsPrivate
== NULL
) {
181 pthread_mutex_init(&prefsPrivate
->lock
, NULL
);
183 prefsPrivate
->name
= NULL
;
184 prefsPrivate
->prefsID
= NULL
;
185 prefsPrivate
->options
= NULL
;
186 prefsPrivate
->path
= NULL
;
187 prefsPrivate
->newPath
= NULL
; // new prefs path
188 prefsPrivate
->locked
= FALSE
;
189 prefsPrivate
->lockFD
= -1;
190 prefsPrivate
->lockPath
= NULL
;
191 prefsPrivate
->signature
= NULL
;
192 prefsPrivate
->session
= NULL
;
193 prefsPrivate
->sessionKeyLock
= NULL
;
194 prefsPrivate
->sessionKeyCommit
= NULL
;
195 prefsPrivate
->sessionKeyApply
= NULL
;
196 prefsPrivate
->scheduled
= FALSE
;
197 prefsPrivate
->rls
= NULL
;
198 prefsPrivate
->rlsFunction
= NULL
;
199 prefsPrivate
->rlsContext
.info
= NULL
;
200 prefsPrivate
->rlsContext
.retain
= NULL
;
201 prefsPrivate
->rlsContext
.release
= NULL
;
202 prefsPrivate
->rlsContext
.copyDescription
= NULL
;
203 prefsPrivate
->rlList
= NULL
;
204 prefsPrivate
->dispatchQueue
= NULL
;
205 prefsPrivate
->prefs
= NULL
;
206 prefsPrivate
->accessed
= FALSE
;
207 prefsPrivate
->changed
= FALSE
;
208 prefsPrivate
->isRoot
= (geteuid() == 0);
209 prefsPrivate
->authorizationData
= NULL
;
210 prefsPrivate
->helper_port
= MACH_PORT_NULL
;
216 __private_extern__ Boolean
217 __SCPreferencesCreate_helper(SCPreferencesRef prefs
)
219 CFDataRef data
= NULL
;
220 CFMutableDictionaryRef info
;
221 char name
[64] = "???";
224 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
225 uint32_t status
= kSCStatusOK
;
227 uint32_t pid
= getpid();
230 ok
= _SCHelperOpen(prefsPrivate
->authorizationData
,
231 &prefsPrivate
->helper_port
);
236 // create a dictionary of information to pass to the helper
237 info
= CFDictionaryCreateMutable(NULL
,
239 &kCFTypeDictionaryKeyCallBacks
,
240 &kCFTypeDictionaryValueCallBacks
);
243 if (prefsPrivate
->prefsID
!= NULL
) {
244 CFDictionarySetValue(info
, CFSTR("prefsID"), prefsPrivate
->prefsID
);
248 if (prefsPrivate
->options
!= NULL
) {
249 CFDictionarySetValue(info
, CFSTR("options"), prefsPrivate
->options
);
252 // save preferences session "name"
253 CFDictionarySetValue(info
, CFSTR("name"), prefsPrivate
->name
);
256 num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &pid
);
257 CFDictionarySetValue(info
, CFSTR("PID"), num
);
261 (void) proc_name(getpid(), name
, sizeof(name
));
262 str
= CFStringCreateWithCString(NULL
, name
, kCFStringEncodingUTF8
);
263 CFDictionarySetValue(info
, CFSTR("PROC_NAME"), str
);
266 // serialize the info
267 ok
= _SCSerialize(info
, &data
, NULL
, NULL
);
269 if (data
== NULL
|| !ok
) {
273 // have the helper "open" the prefs
274 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
275 SCHELPER_MSG_PREFS_OPEN
,
279 if (data
!= NULL
) CFRelease(data
);
284 if (status
!= kSCStatusOK
) {
293 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
294 _SCHelperClose(&prefsPrivate
->helper_port
);
297 status
= kSCStatusAccessError
;
308 __SCPreferencesAccess_helper(SCPreferencesRef prefs
)
311 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
312 CFDictionaryRef serverDict
= NULL
;
313 CFDictionaryRef serverPrefs
= NULL
;
314 CFDictionaryRef serverSignature
= NULL
;
315 uint32_t status
= kSCStatusOK
;
316 CFDataRef reply
= NULL
;
318 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
319 ok
= __SCPreferencesCreate_helper(prefs
);
325 // have the helper "access" the prefs
326 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
327 SCHELPER_MSG_PREFS_ACCESS
,
335 if (status
!= kSCStatusOK
) {
343 ok
= _SCUnserialize((CFPropertyListRef
*)&serverDict
, reply
, NULL
, 0);
349 if (isA_CFDictionary(serverDict
)) {
350 serverPrefs
= CFDictionaryGetValue(serverDict
, CFSTR("preferences"));
351 serverPrefs
= isA_CFDictionary(serverPrefs
);
353 serverSignature
= CFDictionaryGetValue(serverDict
, CFSTR("signature"));
354 serverSignature
= isA_CFData(serverSignature
);
357 if ((serverPrefs
== NULL
) || (serverSignature
== NULL
)) {
358 CFRelease(serverDict
);
362 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, serverPrefs
);
363 prefsPrivate
->signature
= CFRetain(serverSignature
);
364 prefsPrivate
->accessed
= TRUE
;
365 CFRelease(serverDict
);
372 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
373 _SCHelperClose(&prefsPrivate
->helper_port
);
376 status
= kSCStatusAccessError
;
386 static SCPreferencesPrivateRef
387 __SCPreferencesCreate(CFAllocatorRef allocator
,
390 CFDataRef authorizationData
,
391 CFDictionaryRef options
)
394 SCPreferencesPrivateRef prefsPrivate
;
395 int sc_status
= kSCStatusOK
;
398 * allocate and initialize a new prefs session
400 prefsPrivate
= __SCPreferencesCreatePrivate(allocator
);
401 if (prefsPrivate
== NULL
) {
405 prefsPrivate
->name
= CFStringCreateCopy(allocator
, name
);
406 if (prefsID
!= NULL
) {
407 prefsPrivate
->prefsID
= CFStringCreateCopy(allocator
, prefsID
);
409 if (authorizationData
!= NULL
) {
410 prefsPrivate
->authorizationData
= CFRetain(authorizationData
);
412 if (options
!= NULL
) {
413 prefsPrivate
->options
= CFDictionaryCreateCopy(allocator
, options
);
419 * convert prefsID to path
421 prefsPrivate
->path
= __SCPreferencesPath(allocator
,
423 (prefsPrivate
->newPath
== NULL
));
424 if (prefsPrivate
->path
== NULL
) {
425 sc_status
= kSCStatusFailed
;
432 fd
= open(prefsPrivate
->path
, O_RDONLY
, 0644);
439 if ((prefsID
== NULL
) || !CFStringHasPrefix(prefsID
, CFSTR("/"))) {
440 /* if default preference ID or relative path */
441 if (prefsPrivate
->newPath
== NULL
) {
443 * we've looked in the "new" prefs directory
444 * without success. Save the "new" path and
445 * look in the "old" prefs directory.
447 prefsPrivate
->newPath
= prefsPrivate
->path
;
451 * we've looked in both the "new" and "old"
452 * prefs directories without success. Use
455 CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
456 prefsPrivate
->path
= prefsPrivate
->newPath
;
457 prefsPrivate
->newPath
= NULL
;
461 /* no preference data, start fresh */
462 sc_status
= kSCStatusNoConfigFile
;
465 if (prefsPrivate
->authorizationData
!= NULL
) {
466 /* no problem, we'll be using the helper */
470 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCPreferencesCreate open() failed: %s"), strerror(errno
));
471 sc_status
= kSCStatusAccessError
;
474 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCPreferencesCreate open() failed: %s"), strerror(errno
));
475 sc_status
= kSCStatusFailed
;
484 _SCErrorSet(sc_status
);
489 if (fd
!= -1) (void) close(fd
);
490 CFRelease(prefsPrivate
);
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 fd
= open(prefsPrivate
->path
, O_RDONLY
, 0644);
512 if (fstat(fd
, &statBuf
) == -1) {
513 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCPreferencesAccess fstat() failed: %s"), strerror(errno
));
514 bzero(&statBuf
, sizeof(statBuf
));
519 /* no preference data, start fresh */
522 if (prefsPrivate
->authorizationData
!= NULL
) {
523 if (__SCPreferencesAccess_helper(prefs
)) {
527 CFSTR("__SCPreferencesAccess_helper() failed: %s"),
528 SCErrorString(SCError()));
534 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCPreferencesAccess open() failed: %s"), strerror(errno
));
537 bzero(&statBuf
, sizeof(statBuf
));
540 if (prefsPrivate
->signature
!= NULL
) CFRelease(prefsPrivate
->signature
);
541 prefsPrivate
->signature
= __SCPSignatureFromStatbuf(&statBuf
);
543 if (statBuf
.st_size
> 0) {
544 CFDictionaryRef dict
;
546 CFMutableDataRef xmlData
;
549 * extract property list
551 xmlData
= CFDataCreateMutable(allocator
, statBuf
.st_size
);
552 CFDataSetLength(xmlData
, statBuf
.st_size
);
553 if (read(fd
, (void *)CFDataGetBytePtr(xmlData
), statBuf
.st_size
) != statBuf
.st_size
) {
554 /* corrupt prefs file, start fresh */
555 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCPreferencesAccess read(): could not load preference data."));
564 dict
= CFPropertyListCreateWithData(allocator
, xmlData
, kCFPropertyListImmutable
, NULL
, &error
);
567 /* corrupt prefs file, start fresh */
570 CFSTR("__SCPreferencesAccess CFPropertyListCreateWithData(): %@"),
578 * make sure that we've got a dictionary
580 if (!isA_CFDictionary(dict
)) {
581 /* corrupt prefs file, start fresh */
582 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCPreferencesAccess CFGetTypeID(): not a dictionary."));
587 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(allocator
, 0, dict
);
597 if (prefsPrivate
->prefs
== NULL
) {
599 * new file, create empty preferences
601 // SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess(): creating new preferences file."));
602 prefsPrivate
->prefs
= CFDictionaryCreateMutable(allocator
,
604 &kCFTypeDictionaryKeyCallBacks
,
605 &kCFTypeDictionaryValueCallBacks
);
606 prefsPrivate
->changed
= TRUE
;
609 prefsPrivate
->accessed
= TRUE
;
615 SCPreferencesCreate(CFAllocatorRef allocator
,
619 SCPreferencesPrivateRef prefsPrivate
;
621 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, NULL
, NULL
);
622 return (SCPreferencesRef
)prefsPrivate
;
627 SCPreferencesCreateWithAuthorization(CFAllocatorRef allocator
,
630 AuthorizationRef authorization
)
632 SCPreferencesRef prefs
;
635 authorization
= (AuthorizationRef
)1;
636 #endif // TARGET_OS_IPHONE
638 prefs
= SCPreferencesCreateWithOptions(allocator
, name
, prefsID
, authorization
, NULL
);
644 SCPreferencesCreateWithOptions(CFAllocatorRef allocator
,
647 AuthorizationRef authorization
,
648 CFDictionaryRef options
)
650 CFDataRef authorizationData
= NULL
;
651 SCPreferencesPrivateRef prefsPrivate
;
653 if (options
!= NULL
) {
654 if (!isA_CFDictionary(options
)) {
655 _SCErrorSet(kSCStatusInvalidArgument
);
660 if (authorization
!= NULL
) {
661 #if !TARGET_OS_IPHONE
662 AuthorizationExternalForm extForm
;
665 os_status
= AuthorizationMakeExternalForm(authorization
, &extForm
);
666 if (os_status
!= errAuthorizationSuccess
) {
667 SCLog(TRUE
, LOG_INFO
, CFSTR("_SCHelperOpen AuthorizationMakeExternalForm() failed"));
668 _SCErrorSet(kSCStatusInvalidArgument
);
672 authorizationData
= CFDataCreate(NULL
, (const UInt8
*)extForm
.bytes
, sizeof(extForm
.bytes
));
673 #else // !TARGET_OS_IPHONE
675 CFStringRef bundleID
= NULL
;
677 /* get the application/executable/bundle name */
678 bundle
= CFBundleGetMainBundle();
679 if (bundle
!= NULL
) {
680 bundleID
= CFBundleGetIdentifier(bundle
);
681 if (bundleID
!= NULL
) {
686 url
= CFBundleCopyExecutableURL(bundle
);
688 bundleID
= CFURLCopyPath(url
);
693 if (bundleID
!= NULL
) {
694 if (CFEqual(bundleID
, CFSTR("/"))) {
700 if (bundleID
== NULL
) {
701 bundleID
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Unknown(%d)"), getpid());
704 _SCSerializeString(bundleID
, &authorizationData
, NULL
, NULL
);
706 #endif // !TARGET_OS_IPHONE
709 prefsPrivate
= __SCPreferencesCreate(allocator
, name
, prefsID
, authorizationData
, options
);
710 if (authorizationData
!= NULL
) CFRelease(authorizationData
);
712 return (SCPreferencesRef
)prefsPrivate
;
717 SCPreferencesGetTypeID(void) {
718 pthread_once(&initialized
, __SCPreferencesInitialize
); /* initialize runtime */
719 return __kSCPreferencesTypeID
;
724 prefsNotify(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
727 void (*context_release
)(const void *);
730 SCPreferencesNotification notify
= 0;
731 SCPreferencesRef prefs
= (SCPreferencesRef
)info
;
732 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
733 SCPreferencesCallBack rlsFunction
;
735 n
= (changedKeys
!= NULL
) ? CFArrayGetCount(changedKeys
) : 0;
736 for (i
= 0; i
< n
; i
++) {
739 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
740 if (CFEqual(key
, prefsPrivate
->sessionKeyCommit
)) {
741 // if preferences have been saved
742 notify
|= kSCPreferencesNotificationCommit
;
744 if (CFEqual(key
, prefsPrivate
->sessionKeyApply
)) {
745 // if stored preferences should be applied to current configuration
746 notify
|= kSCPreferencesNotificationApply
;
755 pthread_mutex_lock(&prefsPrivate
->lock
);
758 rlsFunction
= prefsPrivate
->rlsFunction
;
759 if (prefsPrivate
->rlsContext
.retain
!= NULL
) {
760 context_info
= (void *)prefsPrivate
->rlsContext
.retain(prefsPrivate
->rlsContext
.info
);
761 context_release
= prefsPrivate
->rlsContext
.release
;
763 context_info
= prefsPrivate
->rlsContext
.info
;
764 context_release
= NULL
;
767 pthread_mutex_unlock(&prefsPrivate
->lock
);
769 if (rlsFunction
!= NULL
) {
770 (*rlsFunction
)(prefs
, notify
, context_info
);
773 if (context_release
!= NULL
) {
774 (*context_release
)(context_info
);
781 __private_extern__ Boolean
782 __SCPreferencesAddSession(SCPreferencesRef prefs
)
784 CFAllocatorRef allocator
= CFGetAllocator(prefs
);
785 SCDynamicStoreContext context
= { 0
791 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
793 /* establish a dynamic store session */
794 prefsPrivate
->session
= SCDynamicStoreCreate(allocator
,
798 if (prefsPrivate
->session
== NULL
) {
799 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("__SCPreferencesAddSession SCDynamicStoreCreate() failed"));
803 /* create the session "commit" key */
804 prefsPrivate
->sessionKeyCommit
= _SCPNotificationKey(NULL
,
805 prefsPrivate
->prefsID
,
806 kSCPreferencesKeyCommit
);
808 /* create the session "apply" key */
809 prefsPrivate
->sessionKeyApply
= _SCPNotificationKey(NULL
,
810 prefsPrivate
->prefsID
,
811 kSCPreferencesKeyApply
);
818 SCPreferencesSetCallback(SCPreferencesRef prefs
,
819 SCPreferencesCallBack callout
,
820 SCPreferencesContext
*context
)
822 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
824 if (!isA_SCPreferences(prefs
)) {
825 /* sorry, you must provide a session */
826 _SCErrorSet(kSCStatusNoPrefsSession
);
830 pthread_mutex_lock(&prefsPrivate
->lock
);
832 if (prefsPrivate
->rlsContext
.release
!= NULL
) {
833 /* let go of the current context */
834 (*prefsPrivate
->rlsContext
.release
)(prefsPrivate
->rlsContext
.info
);
837 prefsPrivate
->rlsFunction
= callout
;
838 prefsPrivate
->rlsContext
.info
= NULL
;
839 prefsPrivate
->rlsContext
.retain
= NULL
;
840 prefsPrivate
->rlsContext
.release
= NULL
;
841 prefsPrivate
->rlsContext
.copyDescription
= NULL
;
842 if (context
!= NULL
) {
843 bcopy(context
, &prefsPrivate
->rlsContext
, sizeof(SCPreferencesContext
));
844 if (context
->retain
!= NULL
) {
845 prefsPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
849 pthread_mutex_unlock(&prefsPrivate
->lock
);
856 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
857 CFRunLoopRef runLoop
,
858 CFStringRef runLoopMode
,
859 dispatch_queue_t queue
)
862 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
864 pthread_mutex_lock(&prefsPrivate
->lock
);
866 if ((prefsPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
867 ((queue
!= NULL
) && prefsPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
868 _SCErrorSet(kSCStatusInvalidArgument
);
872 if (!prefsPrivate
->scheduled
) {
873 CFMutableArrayRef keys
;
875 if (prefsPrivate
->session
== NULL
) {
876 ok
= __SCPreferencesAddSession(prefs
);
882 CFRetain(prefs
); // hold a reference to the prefs
884 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
885 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyCommit
);
886 CFArrayAppendValue(keys
, prefsPrivate
->sessionKeyApply
);
887 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, keys
, NULL
);
890 if (runLoop
!= NULL
) {
891 prefsPrivate
->rls
= SCDynamicStoreCreateRunLoopSource(NULL
, prefsPrivate
->session
, 0);
892 prefsPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
895 prefsPrivate
->scheduled
= TRUE
;
899 ok
= SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, queue
);
901 prefsPrivate
->scheduled
= FALSE
;
902 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
907 prefsPrivate
->dispatchQueue
= queue
;
908 dispatch_retain(prefsPrivate
->dispatchQueue
);
910 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
912 * if we do not already have notifications scheduled with
913 * this runLoop / runLoopMode
915 CFRunLoopAddSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
918 _SC_schedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
);
925 pthread_mutex_unlock(&prefsPrivate
->lock
);
931 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
932 CFRunLoopRef runLoop
,
933 CFStringRef runLoopMode
)
935 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
939 pthread_mutex_lock(&prefsPrivate
->lock
);
941 if ((runLoop
!= NULL
) && !prefsPrivate
->scheduled
) { // if we should be scheduled (but are not)
942 _SCErrorSet(kSCStatusInvalidArgument
);
946 if (((runLoop
== NULL
) && (prefsPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
947 ((runLoop
!= NULL
) && (prefsPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
948 _SCErrorSet(kSCStatusInvalidArgument
);
952 if (runLoop
== NULL
) {
953 SCDynamicStoreSetDispatchQueue(prefsPrivate
->session
, NULL
);
954 dispatch_release(prefsPrivate
->dispatchQueue
);
955 prefsPrivate
->dispatchQueue
= NULL
;
957 if (!_SC_unschedule(prefs
, runLoop
, runLoopMode
, prefsPrivate
->rlList
, FALSE
)) {
958 // if not currently scheduled on this runLoop / runLoopMode
959 _SCErrorSet(kSCStatusInvalidArgument
);
963 n
= CFArrayGetCount(prefsPrivate
->rlList
);
964 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, prefsPrivate
->rlList
)) {
966 * if we are no longer scheduled to receive notifications for
967 * this runLoop / runLoopMode
969 CFRunLoopRemoveSource(runLoop
, prefsPrivate
->rls
, runLoopMode
);
972 // if *all* notifications have been unscheduled
973 CFRelease(prefsPrivate
->rlList
);
974 prefsPrivate
->rlList
= NULL
;
975 CFRunLoopSourceInvalidate(prefsPrivate
->rls
);
976 CFRelease(prefsPrivate
->rls
);
977 prefsPrivate
->rls
= NULL
;
983 CFArrayRef changedKeys
;
985 // if *all* notifications have been unscheduled
986 prefsPrivate
->scheduled
= FALSE
;
988 // no need to track changes
989 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate
->session
, NULL
, NULL
);
991 // clear out any pending notifications
992 changedKeys
= SCDynamicStoreCopyNotifiedKeys(prefsPrivate
->session
);
993 if (changedKeys
!= NULL
) {
994 CFRelease(changedKeys
);
997 // release our reference to the prefs
1005 pthread_mutex_unlock(&prefsPrivate
->lock
);
1011 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs
,
1012 CFRunLoopRef runLoop
,
1013 CFStringRef runLoopMode
)
1015 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1016 _SCErrorSet(kSCStatusInvalidArgument
);
1020 return __SCPreferencesScheduleWithRunLoop(prefs
, runLoop
, runLoopMode
, NULL
);
1025 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs
,
1026 CFRunLoopRef runLoop
,
1027 CFStringRef runLoopMode
)
1029 if (!isA_SCPreferences(prefs
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1030 _SCErrorSet(kSCStatusInvalidArgument
);
1034 return __SCPreferencesUnscheduleFromRunLoop(prefs
, runLoop
, runLoopMode
);
1039 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs
,
1040 dispatch_queue_t queue
)
1044 if (!isA_SCPreferences(prefs
)) {
1045 /* sorry, you must provide a session */
1046 _SCErrorSet(kSCStatusNoPrefsSession
);
1050 if (queue
!= NULL
) {
1051 ok
= __SCPreferencesScheduleWithRunLoop(prefs
, NULL
, NULL
, queue
);
1053 ok
= __SCPreferencesUnscheduleFromRunLoop(prefs
, NULL
, NULL
);
1061 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs
)
1064 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1065 uint32_t status
= kSCStatusOK
;
1067 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
1072 // have the helper "synchronize" the prefs
1073 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
1074 SCHELPER_MSG_PREFS_SYNCHRONIZE
,
1080 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
1081 _SCHelperClose(&prefsPrivate
->helper_port
);
1090 SCPreferencesSynchronize(SCPreferencesRef prefs
)
1092 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1094 if (!isA_SCPreferences(prefs
)) {
1095 /* sorry, you must provide a session */
1096 _SCErrorSet(kSCStatusNoPrefsSession
);
1100 if (prefsPrivate
->authorizationData
!= NULL
) {
1101 __SCPreferencesSynchronize_helper(prefs
);
1103 if (prefsPrivate
->prefs
!= NULL
) {
1104 CFRelease(prefsPrivate
->prefs
);
1105 prefsPrivate
->prefs
= NULL
;
1107 if (prefsPrivate
->signature
!= NULL
) {
1108 CFRelease(prefsPrivate
->signature
);
1109 prefsPrivate
->signature
= NULL
;
1111 prefsPrivate
->accessed
= FALSE
;
1112 prefsPrivate
->changed
= FALSE
;