]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPOpen.c
configd-1109.40.9.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCPOpen.c
1 /*
2 * Copyright(c) 2000-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * February 16, 2004 Allan Nathanson <ajn@apple.com>
28 * - add preference notification APIs
29 *
30 * June 1, 2001 Allan Nathanson <ajn@apple.com>
31 * - public API conversion
32 *
33 * November 9, 2000 Allan Nathanson <ajn@apple.com>
34 * - initial revision
35 */
36
37 #include <TargetConditionals.h>
38 #include <fcntl.h>
39 #include <pthread.h>
40 #include <sandbox.h>
41 #include <unistd.h>
42 #include <sys/errno.h>
43 #include <sys/cdefs.h>
44 #include <dispatch/dispatch.h>
45
46 #include "SCPreferencesInternal.h"
47 #include "SCD.h"
48 #include "SCHelper_client.h"
49 #include "dy_framework.h"
50
51
52 const AuthorizationRef kSCPreferencesUseEntitlementAuthorization = (AuthorizationRef)CFSTR("UseEntitlement");
53
54
55 __private_extern__ os_log_t
56 __log_SCPreferences(void)
57 {
58 static os_log_t log = NULL;
59
60 if (log == NULL) {
61 log = os_log_create("com.apple.SystemConfiguration", "SCPreferences");
62 }
63
64 return log;
65 }
66
67
68 static CFStringRef
69 __SCPreferencesCopyDescription(CFTypeRef cf) {
70 CFAllocatorRef allocator = CFGetAllocator(cf);
71 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)cf;
72 CFMutableStringRef result;
73
74 result = CFStringCreateMutable(allocator, 0);
75 CFStringAppendFormat(result, NULL, CFSTR("<SCPreferences %p [%p]> {"), cf, allocator);
76 CFStringAppendFormat(result, NULL, CFSTR("name = %@"), prefsPrivate->name);
77 CFStringAppendFormat(result, NULL, CFSTR(", id = %@"),
78 prefsPrivate->prefsID != NULL ? prefsPrivate->prefsID : CFSTR("[default]"));
79 CFStringAppendFormat(result, NULL, CFSTR(", path = %s"),
80 prefsPrivate->newPath != NULL ? prefsPrivate->newPath : prefsPrivate->path);
81 if (prefsPrivate->accessed) {
82 CFStringAppendFormat(result, NULL, CFSTR(", accessed"));
83 }
84 if (prefsPrivate->changed) {
85 CFStringAppendFormat(result, NULL, CFSTR(", changed"));
86 }
87 if (prefsPrivate->locked) {
88 CFStringAppendFormat(result, NULL, CFSTR(", locked"));
89 }
90 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
91 CFStringAppendFormat(result, NULL, CFSTR(", helper port = 0x%x"), prefsPrivate->helper_port);
92 }
93 CFStringAppendFormat(result, NULL, CFSTR("}"));
94
95 return result;
96 }
97
98
99 static void
100 __SCPreferencesDeallocate(CFTypeRef cf)
101 {
102 SCPreferencesRef prefs = (SCPreferencesRef)cf;
103 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
104
105 SC_log(LOG_DEBUG, "release %@", prefsPrivate);
106
107 if (prefsPrivate->locked) {
108 __SCPreferencesUpdateLockedState(prefs, FALSE);
109 }
110
111 /* release resources */
112
113 pthread_mutex_destroy(&prefsPrivate->lock);
114
115 if (prefsPrivate->parent != NULL) {
116 SCPreferencesPrivateRef parentPrivate = (SCPreferencesPrivateRef)prefsPrivate->parent;
117
118 // remove [weak] reference from parent to this companion
119 pthread_mutex_lock(&parentPrivate->lock);
120 CFDictionaryRemoveValue(parentPrivate->companions, prefsPrivate->prefsID);
121 pthread_mutex_unlock(&parentPrivate->lock);
122
123 // remove [strong] reference from companion to parent
124 CFRelease(prefsPrivate->parent);
125 }
126 if (prefsPrivate->companions != NULL) CFRelease(prefsPrivate->companions);
127
128 if (prefsPrivate->name) CFRelease(prefsPrivate->name);
129 if (prefsPrivate->prefsID) CFRelease(prefsPrivate->prefsID);
130 if (prefsPrivate->options) CFRelease(prefsPrivate->options);
131 if (prefsPrivate->path) CFAllocatorDeallocate(NULL, prefsPrivate->path);
132 if (prefsPrivate->newPath) CFAllocatorDeallocate(NULL, prefsPrivate->newPath);
133 if (prefsPrivate->lockFD != -1) {
134 if (prefsPrivate->lockPath != NULL) {
135 unlink(prefsPrivate->lockPath);
136 }
137 close(prefsPrivate->lockFD);
138 }
139 if (prefsPrivate->lockPath) CFAllocatorDeallocate(NULL, prefsPrivate->lockPath);
140 if (prefsPrivate->signature) CFRelease(prefsPrivate->signature);
141 if (prefsPrivate->sessionNoO_EXLOCK != NULL) {
142 CFRelease(prefsPrivate->sessionNoO_EXLOCK);
143 }
144 if (prefsPrivate->sessionKeyLock) CFRelease(prefsPrivate->sessionKeyLock);
145 if (prefsPrivate->sessionKeyCommit) CFRelease(prefsPrivate->sessionKeyCommit);
146 if (prefsPrivate->sessionKeyApply) CFRelease(prefsPrivate->sessionKeyApply);
147 if (prefsPrivate->rlsContext.release != NULL) {
148 (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info);
149 }
150 if (prefsPrivate->prefs) CFRelease(prefsPrivate->prefs);
151 if (prefsPrivate->authorizationData != NULL) CFRelease(prefsPrivate->authorizationData);
152 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
153 (void) _SCHelperExec(prefsPrivate->helper_port,
154 SCHELPER_MSG_PREFS_CLOSE,
155 NULL,
156 NULL,
157 NULL);
158 _SCHelperClose(&prefsPrivate->helper_port);
159 }
160
161 return;
162 }
163
164
165 static CFTypeID __kSCPreferencesTypeID = _kCFRuntimeNotATypeID;
166
167
168 static const CFRuntimeClass __SCPreferencesClass = {
169 0, // version
170 "SCPreferences", // className
171 NULL, // init
172 NULL, // copy
173 __SCPreferencesDeallocate, // dealloc
174 NULL, // equal
175 NULL, // hash
176 NULL, // copyFormattingDesc
177 __SCPreferencesCopyDescription // copyDebugDesc
178 };
179
180
181 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
182
183 static void
184 __SCPreferencesInitialize(void) {
185 /* register with CoreFoundation */
186 __kSCPreferencesTypeID = _CFRuntimeRegisterClass(&__SCPreferencesClass);
187 return;
188 }
189
190
191 static SCPreferencesPrivateRef
192 __SCPreferencesCreatePrivate(CFAllocatorRef allocator)
193 {
194 SCPreferencesPrivateRef prefsPrivate;
195 uint32_t size;
196
197 /* initialize runtime */
198 pthread_once(&initialized, __SCPreferencesInitialize);
199
200 /* allocate prefs session */
201 size = sizeof(SCPreferencesPrivate) - sizeof(CFRuntimeBase);
202 prefsPrivate = (SCPreferencesPrivateRef)_CFRuntimeCreateInstance(allocator,
203 __kSCPreferencesTypeID,
204 size,
205 NULL);
206 if (prefsPrivate == NULL) {
207 return NULL;
208 }
209
210 /* initialize non-zero/NULL members */
211 pthread_mutex_init(&prefsPrivate->lock, NULL);
212 prefsPrivate->lockFD = -1;
213 prefsPrivate->isRoot = (geteuid() == 0);
214
215 return prefsPrivate;
216 }
217
218
219 __private_extern__ Boolean
220 __SCPreferencesCreate_helper(SCPreferencesRef prefs)
221 {
222 CFDataRef data = NULL;
223 CFMutableDictionaryRef info;
224 CFNumberRef num;
225 Boolean ok;
226 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
227 uint32_t status = kSCStatusOK;
228 CFStringRef str;
229 uint32_t pid = getpid();
230
231 // start helper
232 ok = _SCHelperOpen(prefsPrivate->authorizationData,
233 &prefsPrivate->helper_port);
234 if (!ok) {
235 goto fail;
236 }
237
238 // create a dictionary of information to pass to the helper
239 info = CFDictionaryCreateMutable(NULL,
240 0,
241 &kCFTypeDictionaryKeyCallBacks,
242 &kCFTypeDictionaryValueCallBacks);
243
244 // save prefsID
245 if (prefsPrivate->prefsID != NULL) {
246 CFDictionarySetValue(info, CFSTR("prefsID"), prefsPrivate->prefsID);
247 }
248
249 // save options
250 if (prefsPrivate->options != NULL) {
251 CFDictionarySetValue(info, CFSTR("options"), prefsPrivate->options);
252 }
253
254 // save preferences session "name"
255 CFDictionarySetValue(info, CFSTR("name"), prefsPrivate->name);
256
257 // save PID
258 num = CFNumberCreate(NULL, kCFNumberSInt32Type, &pid);
259 CFDictionarySetValue(info, CFSTR("PID"), num);
260 CFRelease(num);
261
262 // save process name
263 str = CFStringCreateWithCString(NULL, getprogname(), kCFStringEncodingUTF8);
264 CFDictionarySetValue(info, CFSTR("PROC_NAME"), str);
265 CFRelease(str);
266
267 // serialize the info
268 ok = _SCSerialize(info, &data, NULL, NULL);
269 CFRelease(info);
270 if (data == NULL || !ok) {
271 goto fail;
272 }
273
274 // have the helper "open" the prefs
275 ok = _SCHelperExec(prefsPrivate->helper_port,
276 SCHELPER_MSG_PREFS_OPEN,
277 data,
278 &status,
279 NULL);
280 if (data != NULL) CFRelease(data);
281 if (!ok) {
282 goto fail;
283 }
284
285 if (status != kSCStatusOK) {
286 goto error;
287 }
288
289 return TRUE;
290
291 fail :
292
293 // close helper
294 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
295 _SCHelperClose(&prefsPrivate->helper_port);
296 }
297
298 status = kSCStatusAccessError;
299
300 error :
301
302 // return error
303 _SCErrorSet(status);
304 return FALSE;
305 }
306
307
308 static Boolean
309 __SCPreferencesAccess_helper(SCPreferencesRef prefs)
310 {
311 Boolean ok;
312 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
313 CFDictionaryRef serverDict = NULL;
314 CFDictionaryRef serverPrefs = NULL;
315 CFDictionaryRef serverSignature = NULL;
316 uint32_t status = kSCStatusOK;
317 CFDataRef reply = NULL;
318
319 if (prefsPrivate->helper_port == MACH_PORT_NULL) {
320 ok = __SCPreferencesCreate_helper(prefs);
321 if (!ok) {
322 return FALSE;
323 }
324 }
325
326 // have the helper "access" the prefs
327 ok = _SCHelperExec(prefsPrivate->helper_port,
328 SCHELPER_MSG_PREFS_ACCESS,
329 NULL,
330 &status,
331 &reply);
332 if (!ok) {
333 goto fail;
334 }
335
336 if (status != kSCStatusOK) {
337 goto error;
338 }
339
340 if (reply == NULL) {
341 goto fail;
342 }
343
344 ok = _SCUnserialize((CFPropertyListRef *)&serverDict, reply, NULL, 0);
345 CFRelease(reply);
346 if (!ok) {
347 goto fail;
348 }
349
350 if (isA_CFDictionary(serverDict)) {
351 serverPrefs = CFDictionaryGetValue(serverDict, CFSTR("preferences"));
352 serverPrefs = isA_CFDictionary(serverPrefs);
353
354 serverSignature = CFDictionaryGetValue(serverDict, CFSTR("signature"));
355 serverSignature = isA_CFData(serverSignature);
356 }
357
358 if ((serverPrefs == NULL) || (serverSignature == NULL)) {
359 if (serverDict != NULL) CFRelease(serverDict);
360 goto fail;
361 }
362
363 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, serverPrefs);
364 prefsPrivate->signature = CFRetain(serverSignature);
365 prefsPrivate->accessed = TRUE;
366 CFRelease(serverDict);
367
368 return TRUE;
369
370 fail :
371
372 // close helper
373 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
374 _SCHelperClose(&prefsPrivate->helper_port);
375 }
376
377 status = kSCStatusAccessError;
378
379 error :
380
381 // return error
382 _SCErrorSet(status);
383 return FALSE;
384 }
385
386
387 static SCPreferencesPrivateRef
388 __SCPreferencesCreate(CFAllocatorRef allocator,
389 CFStringRef name,
390 CFStringRef prefsID,
391 CFDataRef authorizationData,
392 CFDictionaryRef options)
393 {
394 SCPreferencesPrivateRef prefsPrivate;
395 int sc_status = kSCStatusOK;
396
397 /*
398 * allocate and initialize a new prefs session
399 */
400 prefsPrivate = __SCPreferencesCreatePrivate(allocator);
401 if (prefsPrivate == NULL) {
402 return NULL;
403 }
404
405 prefsPrivate->name = CFStringCreateCopy(allocator, name);
406 if (prefsID != NULL) {
407 prefsPrivate->prefsID = CFStringCreateCopy(allocator, prefsID);
408 }
409 if (authorizationData != NULL) {
410 prefsPrivate->authorizationData = CFRetain(authorizationData);
411 }
412 if (options != NULL) {
413 prefsPrivate->options = CFDictionaryCreateCopy(allocator, options);
414 }
415
416 retry :
417
418 /*
419 * convert prefsID to path
420 */
421 prefsPrivate->path = __SCPreferencesPath(allocator,
422 prefsID,
423 (prefsPrivate->newPath == NULL));
424 if (prefsPrivate->path == NULL) {
425 sc_status = kSCStatusFailed;
426 goto error;
427 }
428
429 if (access(prefsPrivate->path, R_OK) == 0) {
430 goto done;
431 }
432
433 switch (errno) {
434 case ENOENT :
435 /* no prefs file */
436 if ((prefsID == NULL) || !CFStringHasPrefix(prefsID, CFSTR("/"))) {
437 /* if default preference ID or relative path */
438 if (prefsPrivate->newPath == NULL) {
439 /*
440 * we've looked in the "new" prefs directory
441 * without success. Save the "new" path and
442 * look in the "old" prefs directory.
443 */
444 prefsPrivate->newPath = prefsPrivate->path;
445 goto retry;
446 } else {
447 /*
448 * we've looked in both the "new" and "old"
449 * prefs directories without success. Use
450 * the "new" path.
451 */
452 CFAllocatorDeallocate(NULL, prefsPrivate->path);
453 prefsPrivate->path = prefsPrivate->newPath;
454 prefsPrivate->newPath = NULL;
455 }
456 }
457
458 /* no preference data, start fresh */
459 sc_status = kSCStatusNoConfigFile;
460 goto done;
461 case EPERM :
462 case EACCES :
463 if (prefsPrivate->authorizationData != NULL) {
464 /* no problem, we'll be using the helper */
465 goto done;
466 }
467
468 SC_log(LOG_NOTICE, "open() failed: %s", strerror(errno));
469 sc_status = kSCStatusAccessError;
470 break;
471 default :
472 SC_log(LOG_NOTICE, "open() failed: %s", strerror(errno));
473 sc_status = kSCStatusFailed;
474 break;
475 }
476
477 error:
478
479 CFRelease(prefsPrivate);
480 _SCErrorSet(sc_status);
481 return NULL;
482
483 done :
484
485 /* all OK */
486 _SCErrorSet(sc_status);
487 return prefsPrivate;
488 }
489
490
491 __private_extern__ void
492 __SCPreferencesAccess(SCPreferencesRef prefs)
493 {
494 CFAllocatorRef allocator = CFGetAllocator(prefs);
495 int fd = -1;
496 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
497 struct stat statBuf;
498
499 if (prefsPrivate->accessed) {
500 // if preference data has already been accessed
501 return;
502 }
503
504 if (access(prefsPrivate->path, R_OK) == 0) {
505 fd = open(prefsPrivate->path, O_RDONLY, 0644);
506 } else {
507 fd = -1;
508 }
509 if (fd != -1) {
510 // create signature
511 if (fstat(fd, &statBuf) == -1) {
512 SC_log(LOG_NOTICE, "fstat() failed: %s", strerror(errno));
513 memset(&statBuf, 0, sizeof(statBuf));
514 }
515 } else {
516 switch (errno) {
517 case ENOENT :
518 /* no preference data, start fresh */
519 break;
520 case EPERM :
521 case EACCES :
522 if (prefsPrivate->authorizationData != NULL) {
523 if (__SCPreferencesAccess_helper(prefs)) {
524 goto done;
525 } else {
526 SC_log(LOG_NOTICE, "__SCPreferencesAccess_helper() failed: %s",
527 SCErrorString(SCError()));
528 }
529 break;
530 }
531 // fall through
532 default :
533 SC_log(LOG_NOTICE, "open() failed: %s", strerror(errno));
534 break;
535 }
536 memset(&statBuf, 0, sizeof(statBuf));
537 }
538
539 if (prefsPrivate->signature != NULL) CFRelease(prefsPrivate->signature);
540 prefsPrivate->signature = __SCPSignatureFromStatbuf(&statBuf);
541
542 if (statBuf.st_size > 0) {
543 CFDictionaryRef dict;
544 CFErrorRef error = NULL;
545 CFMutableDataRef xmlData;
546
547 /*
548 * extract property list
549 */
550 xmlData = CFDataCreateMutable(allocator, (CFIndex)statBuf.st_size);
551 CFDataSetLength(xmlData, (CFIndex)statBuf.st_size);
552 if (read(fd, (void *)CFDataGetBytePtr(xmlData), (CFIndex)statBuf.st_size) != (CFIndex)statBuf.st_size) {
553 /* corrupt prefs file, start fresh */
554 SC_log(LOG_INFO, "read(): could not load preference data");
555 CFRelease(xmlData);
556 xmlData = NULL;
557 goto done;
558 }
559
560 /*
561 * load preferences
562 */
563 dict = CFPropertyListCreateWithData(allocator, xmlData, kCFPropertyListImmutable, NULL, &error);
564 CFRelease(xmlData);
565 if (dict == NULL) {
566 /* corrupt prefs file, start fresh */
567 if (error != NULL) {
568 SC_log(LOG_NOTICE, "CFPropertyListCreateWithData(): %@", error);
569 CFRelease(error);
570 }
571 goto done;
572 }
573
574 /*
575 * make sure that we've got a dictionary
576 */
577 if (!isA_CFDictionary(dict)) {
578 /* corrupt prefs file, start fresh */
579 SC_log(LOG_INFO, "CFGetTypeID(): not a dictionary");
580 CFRelease(dict);
581 goto done;
582 }
583
584 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(allocator, 0, dict);
585 CFRelease(dict);
586 }
587
588 done :
589
590 if (fd != -1) {
591 (void) close(fd);
592 }
593
594 if (prefsPrivate->prefs == NULL) {
595 /*
596 * new file, create empty preferences
597 */
598 // SC_log(LOG_INFO, "creating new preferences file");
599 prefsPrivate->prefs = CFDictionaryCreateMutable(allocator,
600 0,
601 &kCFTypeDictionaryKeyCallBacks,
602 &kCFTypeDictionaryValueCallBacks);
603 prefsPrivate->changed = FALSE;
604 }
605
606 SC_log(LOG_DEBUG, "SCPreferences() access: %s, size=%lld",
607 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path,
608 __SCPreferencesPrefsSize(prefs));
609
610 prefsPrivate->accessed = TRUE;
611 return;
612 }
613
614
615 SCPreferencesRef
616 SCPreferencesCreate(CFAllocatorRef allocator,
617 CFStringRef name,
618 CFStringRef prefsID)
619 {
620 SCPreferencesPrivateRef prefsPrivate;
621
622 prefsPrivate = __SCPreferencesCreate(allocator, name, prefsID, NULL, NULL);
623 if (prefsPrivate != NULL) {
624 SC_log(LOG_DEBUG, "create %@", prefsPrivate);
625 }
626
627 return (SCPreferencesRef)prefsPrivate;
628 }
629
630
631 SCPreferencesRef
632 SCPreferencesCreateWithAuthorization(CFAllocatorRef allocator,
633 CFStringRef name,
634 CFStringRef prefsID,
635 AuthorizationRef authorization)
636 {
637 SCPreferencesRef prefs;
638
639 #if !TARGET_OS_IPHONE
640 if (authorization == NULL) {
641 authorization = kSCPreferencesUseEntitlementAuthorization;
642 }
643 #else // !TARGET_OS_IPHONE
644 authorization = kSCPreferencesUseEntitlementAuthorization;
645 #endif // !TARGET_OS_IPHONE
646
647 prefs = SCPreferencesCreateWithOptions(allocator, name, prefsID, authorization, NULL);
648 return prefs;
649 }
650
651
652 SCPreferencesRef
653 SCPreferencesCreateWithOptions(CFAllocatorRef allocator,
654 CFStringRef name,
655 CFStringRef prefsID,
656 AuthorizationRef authorization,
657 CFDictionaryRef options)
658 {
659 CFDataRef authorizationData = NULL;
660 SCPreferencesPrivateRef prefsPrivate;
661
662 if (options != NULL) {
663 if (!isA_CFDictionary(options)) {
664 _SCErrorSet(kSCStatusInvalidArgument);
665 return NULL;
666 }
667 }
668
669 if (authorization != NULL) {
670 CFMutableDictionaryRef authorizationDict;
671 CFStringRef bundleID;
672
673 authorizationDict = CFDictionaryCreateMutable(NULL,
674 0,
675 &kCFTypeDictionaryKeyCallBacks,
676 &kCFTypeDictionaryValueCallBacks);
677 #if !TARGET_OS_IPHONE
678 if (authorization != kSCPreferencesUseEntitlementAuthorization) {
679 CFDataRef data;
680 AuthorizationExternalForm extForm;
681 OSStatus os_status;
682
683 os_status = AuthorizationMakeExternalForm(authorization, &extForm);
684 if (os_status != errAuthorizationSuccess) {
685 SC_log(LOG_INFO, "AuthorizationMakeExternalForm() failed");
686 _SCErrorSet(kSCStatusInvalidArgument);
687 CFRelease(authorizationDict);
688 return NULL;
689 }
690
691 data = CFDataCreate(NULL, (const UInt8 *)extForm.bytes, sizeof(extForm.bytes));
692 CFDictionaryAddValue(authorizationDict,
693 kSCHelperAuthAuthorization,
694 data);
695 CFRelease(data);
696 }
697 #endif // !TARGET_OS_IPHONE
698
699 bundleID = _SC_getApplicationBundleID();
700 CFDictionaryAddValue(authorizationDict,
701 kSCHelperAuthCallerInfo,
702 bundleID);
703
704 if (authorizationDict != NULL) {
705 (void) _SCSerialize((CFPropertyListRef)authorizationDict,
706 &authorizationData,
707 NULL,
708 NULL);
709 CFRelease(authorizationDict);
710 }
711 }
712
713 prefsPrivate = __SCPreferencesCreate(allocator, name, prefsID, authorizationData, options);
714 if (prefsPrivate != NULL) {
715 const char *opt_none = "";
716 const char *opt_1 = opt_none;
717 const char *opt_2 = opt_none;
718
719 if (options != NULL) {
720 opt_2 = "options";
721 }
722
723 if (authorization != NULL) {
724 if (authorization == kSCPreferencesUseEntitlementAuthorization) {
725 opt_1 = "entitlement";
726 } else {
727 opt_1 = "authorization";
728 }
729 }
730
731 SC_log(LOG_DEBUG, "create w/%s%s%s %@",
732 opt_2,
733 ((opt_2 != opt_none) && (opt_1 != opt_none)) ? " + " : "",
734 opt_1,
735 prefsPrivate);
736 }
737
738 if (authorizationData != NULL) CFRelease(authorizationData);
739
740 return (SCPreferencesRef)prefsPrivate;
741 }
742
743
744 SCPreferencesRef
745 SCPreferencesCreateCompanion(SCPreferencesRef prefs, CFStringRef companionPrefsID)
746 {
747 CFAllocatorRef allocator = CFGetAllocator(prefs);
748 SCPreferencesPrivateRef companionPrefs = NULL;
749 CFMutableStringRef newPrefsID;
750 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
751
752 if (companionPrefsID == NULL) {
753 companionPrefsID = PREFS_DEFAULT_CONFIG;
754 } else {
755 if (CFStringFindWithOptions(companionPrefsID,
756 CFSTR("/"),
757 CFRangeMake(0, CFStringGetLength(companionPrefsID)),
758 kCFCompareBackwards,
759 NULL)) {
760 // if companion prefsID contains a "/"
761 _SCErrorSet(kSCStatusInvalidArgument);
762 return NULL;
763 }
764 }
765
766 if (prefsPrivate->prefsID == NULL) {
767 if (CFEqual(companionPrefsID, PREFS_DEFAULT_CONFIG)) {
768 // if prefsID and companionPrefsID match
769 _SCErrorSet(kSCStatusInvalidArgument);
770 return NULL;
771 }
772 newPrefsID = CFStringCreateMutableCopy(allocator, 0, companionPrefsID);
773 } else {
774 CFIndex prefsIDLen = CFStringGetLength(prefsPrivate->prefsID);
775 CFRange range;
776
777 if (CFStringFindWithOptions(prefsPrivate->prefsID,
778 CFSTR("/"),
779 CFRangeMake(0, prefsIDLen),
780 kCFCompareBackwards,
781 &range)) {
782 Boolean match;
783 CFStringRef suffix;
784
785 // if slash, check suffix
786 range.location++;
787 if (range.location >= prefsIDLen) {
788 // if no suffix
789 _SCErrorSet(kSCStatusInvalidArgument);
790 return NULL;
791 }
792 range.length = prefsIDLen - range.location;
793 suffix = CFStringCreateWithSubstring(allocator, prefsPrivate->prefsID, range);
794 match = CFEqual(suffix, companionPrefsID);
795 CFRelease(suffix);
796 if (match) {
797 // if prefsID [suffix] and companionPrefsID match
798 _SCErrorSet(kSCStatusInvalidArgument);
799 return NULL;
800 }
801
802 // replace the suffix
803 newPrefsID = CFStringCreateMutableCopy(NULL, 0, prefsPrivate->prefsID);
804 CFStringReplace(newPrefsID, range, companionPrefsID);
805 } else if (!CFEqual(prefsPrivate->prefsID, companionPrefsID)) {
806 // if no slash, prefsID and companionPrefsID differ
807 newPrefsID = CFStringCreateMutableCopy(NULL, 0, companionPrefsID);
808 } else {
809 // if no slash, prefsID and companionPrefsID match
810 _SCErrorSet(kSCStatusInvalidArgument);
811 return NULL;
812 }
813 }
814 assert(newPrefsID != NULL);
815
816 pthread_mutex_lock(&prefsPrivate->lock);
817 if ((prefsPrivate->companions != NULL) &&
818 CFDictionaryGetValueIfPresent(prefsPrivate->companions,
819 newPrefsID,
820 (const void **)&companionPrefs) &&
821 (companionPrefs != NULL)) {
822 // if we already have a companion
823 SC_log(LOG_DEBUG, "create [companion] reference %@", companionPrefs);
824 CFRetain(companionPrefs);
825 } else {
826 companionPrefs = __SCPreferencesCreate(allocator,
827 prefsPrivate->name,
828 newPrefsID,
829 prefsPrivate->authorizationData,
830 prefsPrivate->options);
831 if (companionPrefs != NULL) {
832 SCPreferencesPrivateRef companionPrefsPrivate = (SCPreferencesPrivateRef)companionPrefs;
833
834 SC_log(LOG_DEBUG, "create [companion] %@", companionPrefs);
835
836 // add [strong] reference from companion to parent
837 companionPrefsPrivate->parent = CFRetain(prefs);
838
839 // add [weak] reference from parent to this companion
840 if (prefsPrivate->companions == NULL) {
841 prefsPrivate->companions = CFDictionaryCreateMutable(NULL,
842 0,
843 &kCFTypeDictionaryKeyCallBacks,
844 NULL);
845 }
846 CFDictionarySetValue(prefsPrivate->companions, newPrefsID, companionPrefs);
847 }
848 }
849 pthread_mutex_unlock(&prefsPrivate->lock);
850
851 CFRelease(newPrefsID);
852
853 return (SCPreferencesRef)companionPrefs;
854 }
855
856
857 CFTypeID
858 SCPreferencesGetTypeID(void) {
859 pthread_once(&initialized, __SCPreferencesInitialize); /* initialize runtime */
860 return __kSCPreferencesTypeID;
861 }
862
863
864 static void
865 prefsNotify(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
866 {
867 #pragma unused(store)
868 void *context_info;
869 void (*context_release)(const void *);
870 CFIndex i;
871 CFIndex n;
872 SCPreferencesNotification notify = 0;
873 SCPreferencesRef prefs = (SCPreferencesRef)info;
874 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
875 SCPreferencesCallBack rlsFunction;
876
877 n = (changedKeys != NULL) ? CFArrayGetCount(changedKeys) : 0;
878 for (i = 0; i < n; i++) {
879 CFStringRef key;
880
881 key = CFArrayGetValueAtIndex(changedKeys, i);
882
883 // check if "commit"
884 if (CFEqual(key, prefsPrivate->sessionKeyCommit)) {
885 // if preferences have been saved
886 notify |= kSCPreferencesNotificationCommit;
887 continue;
888 }
889
890 // check if "apply"
891 if (CFEqual(key, prefsPrivate->sessionKeyApply)) {
892 // if stored preferences should be applied to current configuration
893 notify |= kSCPreferencesNotificationApply;
894 continue;
895 }
896 }
897
898 if (notify == 0) {
899 // if no changes
900 return;
901 }
902
903 pthread_mutex_lock(&prefsPrivate->lock);
904
905 /* callout */
906 rlsFunction = prefsPrivate->rlsFunction;
907 if (prefsPrivate->rlsContext.retain != NULL) {
908 context_info = (void *)prefsPrivate->rlsContext.retain(prefsPrivate->rlsContext.info);
909 context_release = prefsPrivate->rlsContext.release;
910 } else {
911 context_info = prefsPrivate->rlsContext.info;
912 context_release = NULL;
913 }
914
915 pthread_mutex_unlock(&prefsPrivate->lock);
916
917 if (rlsFunction != NULL) {
918 SC_log(LOG_DEBUG, "exec SCPreferences callout: %s%s%s",
919 ((notify & kSCPreferencesNotificationCommit) != 0) ? "commit" : "",
920 (((notify & kSCPreferencesNotificationCommit) != 0) &&
921 ((notify & kSCPreferencesNotificationApply ) != 0)) ? ", " : "",
922 ((notify & kSCPreferencesNotificationApply) != 0) ? "apply" : "");
923 (*rlsFunction)(prefs, notify, context_info);
924 }
925
926 if (context_release != NULL) {
927 (*context_release)(context_info);
928 }
929
930 return;
931 }
932
933
934 __private_extern__ void
935 __SCPreferencesAddSessionKeys(SCPreferencesRef prefs)
936 {
937 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
938
939 /* create the session "commit" key */
940 if (prefsPrivate->sessionKeyCommit == NULL) {
941 prefsPrivate->sessionKeyCommit = _SCPNotificationKey(NULL,
942 prefsPrivate->prefsID,
943 kSCPreferencesKeyCommit);
944 }
945
946 /* create the session "apply" key */
947 if (prefsPrivate->sessionKeyApply == NULL) {
948 prefsPrivate->sessionKeyApply = _SCPNotificationKey(NULL,
949 prefsPrivate->prefsID,
950 kSCPreferencesKeyApply);
951 }
952
953 return;
954 }
955
956
957 __private_extern__ Boolean
958 __SCPreferencesAddSession(SCPreferencesRef prefs)
959 {
960 CFAllocatorRef allocator = CFGetAllocator(prefs);
961 SCDynamicStoreContext context = { 0
962 , (void *)prefs
963 , CFRetain
964 , CFRelease
965 , CFCopyDescription
966 };
967 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
968
969 if (prefsPrivate->sessionRefcnt == 0) {
970 /* establish a dynamic store session */
971 prefsPrivate->session = SCDynamicStoreCreate(allocator,
972 prefsPrivate->name,
973 prefsNotify,
974 &context);
975 if (prefsPrivate->session == NULL) {
976 SC_log(LOG_ERR, "could not add SCDynamicStore session (for prefs): %s",
977 SCErrorString(SCError()));
978 return FALSE;
979 }
980
981 SC_log(LOG_DEBUG, "added SCDynamicStore session (for prefs)");
982 }
983
984 prefsPrivate->sessionRefcnt++;
985 return TRUE;
986 }
987
988
989 __private_extern__ void
990 __SCPreferencesRemoveSession(SCPreferencesRef prefs)
991 {
992 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
993
994 if (prefsPrivate->sessionRefcnt > 0) {
995 if (--prefsPrivate->sessionRefcnt == 0) {
996 CFRelease(prefsPrivate->session);
997 prefsPrivate->session = NULL;
998
999 SC_log(LOG_DEBUG, "removed SCDynamicStore session (for prefs)");
1000 }
1001 }
1002
1003 return;
1004 }
1005
1006
1007 static void
1008 appendLockedPreferences(const void *key, const void *value, void *context)
1009 {
1010 #pragma unused(key)
1011 CFMutableStringRef str = (CFMutableStringRef)context;
1012
1013 CFStringAppendFormat(str, NULL, CFSTR("%s%@"),
1014 (CFStringGetLength(str) > 0) ? "\n" : "",
1015 value);
1016 return;
1017 }
1018
1019
1020 __private_extern__ void
1021 __SCPreferencesUpdateLockedState(SCPreferencesRef prefs, Boolean locked)
1022 {
1023 static dispatch_queue_t lockedQueue;
1024 static CFMutableDictionaryRef lockedState;
1025 static dispatch_once_t once;
1026 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1027
1028 dispatch_once(&once, ^{
1029 os_state_block_t state_block;
1030
1031 lockedQueue = dispatch_queue_create("SCPreferences locked state queue", NULL);
1032
1033 lockedState = CFDictionaryCreateMutable(NULL,
1034 0,
1035 NULL, // NO retain/release
1036 &kCFTypeDictionaryValueCallBacks);
1037
1038 state_block = ^os_state_data_t(os_state_hints_t hints) {
1039 #pragma unused(hints)
1040 CFDataRef data = NULL;
1041 Boolean ok;
1042 os_state_data_t state_data;
1043 size_t state_data_size;
1044 CFIndex state_len;
1045 CFMutableStringRef str;
1046
1047 if (CFDictionaryGetCount(lockedState) == 0) {
1048 // if no locked preferences
1049 return NULL;
1050 }
1051
1052 str = CFStringCreateMutable(NULL, 0);
1053 CFDictionaryApplyFunction(lockedState, appendLockedPreferences, str);
1054 ok = _SCSerialize(str, &data, NULL, NULL);
1055 CFRelease(str);
1056
1057 state_len = (ok && (data != NULL)) ? CFDataGetLength(data) : 0;
1058 state_data_size = OS_STATE_DATA_SIZE_NEEDED(state_len);
1059 if (state_data_size > MAX_STATEDUMP_SIZE) {
1060 SC_log(LOG_ERR, "locked SCPreferences : state data too large (%zd > %zd)",
1061 state_data_size,
1062 (size_t)MAX_STATEDUMP_SIZE);
1063 if (data != NULL) CFRelease(data);
1064 return NULL;
1065 }
1066
1067 state_data = calloc(1, state_data_size);
1068 if (state_data == NULL) {
1069 SC_log(LOG_ERR, "locked SCPreferences: could not allocate state data");
1070 if (data != NULL) CFRelease(data);
1071 return NULL;
1072 }
1073
1074 state_data->osd_type = OS_STATE_DATA_SERIALIZED_NSCF_OBJECT;
1075 state_data->osd_data_size = (uint32_t)state_len;
1076 strlcpy(state_data->osd_title, "open/locked SCPreferences", sizeof(state_data->osd_title));
1077 if (state_len > 0) {
1078 memcpy(state_data->osd_data, CFDataGetBytePtr(data), state_len);
1079 }
1080 if (data != NULL) CFRelease(data);
1081
1082 return state_data;
1083 };
1084
1085 (void) os_state_add_handler(lockedQueue, state_block);
1086 });
1087
1088 // update the locked state
1089 prefsPrivate->locked = locked;
1090
1091 // add (or update) the locked preferences
1092 dispatch_sync(lockedQueue, ^{
1093 if (locked) {
1094 CFStringRef str;
1095
1096 str = CFCopyDescription(prefs);
1097 CFDictionarySetValue(lockedState, prefs, str);
1098 CFRelease(str);
1099 } else {
1100 CFDictionaryRemoveValue(lockedState, prefs);
1101 }
1102 });
1103
1104 return;
1105 }
1106
1107
1108 Boolean
1109 SCPreferencesSetCallback(SCPreferencesRef prefs,
1110 SCPreferencesCallBack callout,
1111 SCPreferencesContext *context)
1112 {
1113 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1114
1115 if (!isA_SCPreferences(prefs)) {
1116 /* sorry, you must provide a session */
1117 _SCErrorSet(kSCStatusNoPrefsSession);
1118 return FALSE;
1119 }
1120
1121 pthread_mutex_lock(&prefsPrivate->lock);
1122
1123 if (prefsPrivate->rlsContext.release != NULL) {
1124 /* let go of the current context */
1125 (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info);
1126 }
1127
1128 prefsPrivate->rlsFunction = callout;
1129 prefsPrivate->rlsContext.info = NULL;
1130 prefsPrivate->rlsContext.retain = NULL;
1131 prefsPrivate->rlsContext.release = NULL;
1132 prefsPrivate->rlsContext.copyDescription = NULL;
1133 if (context != NULL) {
1134 memcpy(&prefsPrivate->rlsContext, context, sizeof(SCPreferencesContext));
1135 if (context->retain != NULL) {
1136 prefsPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
1137 }
1138 }
1139
1140 pthread_mutex_unlock(&prefsPrivate->lock);
1141
1142 return TRUE;
1143 }
1144
1145
1146 static Boolean
1147 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs,
1148 CFRunLoopRef runLoop,
1149 CFStringRef runLoopMode,
1150 dispatch_queue_t queue)
1151 {
1152 Boolean ok = FALSE;
1153 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1154
1155 pthread_mutex_lock(&prefsPrivate->lock);
1156
1157 if ((prefsPrivate->dispatchQueue != NULL) || // if we are already scheduled on a dispatch queue
1158 ((queue != NULL) && prefsPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
1159 _SCErrorSet(kSCStatusInvalidArgument);
1160 goto done;
1161 }
1162
1163 if (!prefsPrivate->scheduled) {
1164 CFMutableArrayRef keys;
1165
1166 // add SCDynamicStore session (for notifications) ... and hold a 'prefs' reference
1167 if (prefsPrivate->session == NULL) {
1168 ok = __SCPreferencesAddSession(prefs);
1169 if (!ok) {
1170 goto done;
1171 }
1172 assert(prefsPrivate->session != NULL);
1173 }
1174
1175 // add SCDynamicStore "keys"
1176 __SCPreferencesAddSessionKeys(prefs);
1177
1178 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1179 CFArrayAppendValue(keys, prefsPrivate->sessionKeyCommit);
1180 CFArrayAppendValue(keys, prefsPrivate->sessionKeyApply);
1181 ok = SCDynamicStoreSetNotificationKeys(prefsPrivate->session, keys, NULL);
1182 CFRelease(keys);
1183 if (!ok) {
1184 SC_log(LOG_ERR, "could not set SCDynamicStore notification keys (for prefs): %s",
1185 SCErrorString(SCError()));
1186 goto done;
1187 }
1188
1189 if (runLoop != NULL) {
1190 prefsPrivate->rls = SCDynamicStoreCreateRunLoopSource(NULL, prefsPrivate->session, 0);
1191 if (prefsPrivate->rls == NULL) {
1192 SC_log(LOG_ERR, "could not create SCDynamicStore runloop source (for prefs): %s",
1193 SCErrorString(SCError()));
1194 ok = FALSE;
1195 goto done;
1196 }
1197 prefsPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1198 }
1199
1200 SC_log(LOG_DEBUG, "scheduled");
1201
1202 prefsPrivate->scheduled = TRUE;
1203 }
1204
1205 if (queue != NULL) {
1206 ok = SCDynamicStoreSetDispatchQueue(prefsPrivate->session, queue);
1207 if (!ok) {
1208 prefsPrivate->scheduled = FALSE;
1209 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL);
1210 __SCPreferencesRemoveSession(prefs);
1211 goto done;
1212 }
1213
1214 prefsPrivate->dispatchQueue = queue;
1215 dispatch_retain(prefsPrivate->dispatchQueue);
1216 } else {
1217 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) {
1218 /*
1219 * if we do not already have notifications scheduled with
1220 * this runLoop / runLoopMode
1221 */
1222 CFRunLoopAddSource(runLoop, prefsPrivate->rls, runLoopMode);
1223 }
1224
1225 _SC_schedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList);
1226 }
1227
1228 ok = TRUE;
1229
1230 done :
1231
1232 pthread_mutex_unlock(&prefsPrivate->lock);
1233 return ok;
1234 }
1235
1236
1237 static Boolean
1238 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs,
1239 CFRunLoopRef runLoop,
1240 CFStringRef runLoopMode)
1241 {
1242 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1243 CFIndex n = 0;
1244 Boolean ok = FALSE;
1245
1246 pthread_mutex_lock(&prefsPrivate->lock);
1247
1248 if ((runLoop != NULL) && !prefsPrivate->scheduled) { // if we should be scheduled (but are not)
1249 _SCErrorSet(kSCStatusInvalidArgument);
1250 goto done;
1251 }
1252
1253 if (((runLoop == NULL) && (prefsPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
1254 ((runLoop != NULL) && (prefsPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1255 _SCErrorSet(kSCStatusInvalidArgument);
1256 goto done;
1257 }
1258
1259 if (runLoop == NULL) {
1260 SCDynamicStoreSetDispatchQueue(prefsPrivate->session, NULL);
1261 dispatch_release(prefsPrivate->dispatchQueue);
1262 prefsPrivate->dispatchQueue = NULL;
1263 } else {
1264 if (!_SC_unschedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList, FALSE)) {
1265 // if not currently scheduled on this runLoop / runLoopMode
1266 _SCErrorSet(kSCStatusInvalidArgument);
1267 goto done;
1268 }
1269
1270 n = CFArrayGetCount(prefsPrivate->rlList);
1271 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) {
1272 /*
1273 * if we are no longer scheduled to receive notifications for
1274 * this runLoop / runLoopMode
1275 */
1276 CFRunLoopRemoveSource(runLoop, prefsPrivate->rls, runLoopMode);
1277
1278 if (n == 0) {
1279 // if *all* notifications have been unscheduled
1280 CFRelease(prefsPrivate->rlList);
1281 prefsPrivate->rlList = NULL;
1282 CFRunLoopSourceInvalidate(prefsPrivate->rls);
1283 CFRelease(prefsPrivate->rls);
1284 prefsPrivate->rls = NULL;
1285 }
1286 }
1287 }
1288
1289 if (n == 0) {
1290 CFArrayRef changedKeys;
1291
1292 SC_log(LOG_DEBUG, "unscheduled");
1293
1294 // if *all* notifications have been unscheduled
1295 prefsPrivate->scheduled = FALSE;
1296
1297 // no need to track changes
1298 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL);
1299
1300 // clear out any pending notifications
1301 changedKeys = SCDynamicStoreCopyNotifiedKeys(prefsPrivate->session);
1302 if (changedKeys != NULL) {
1303 CFRelease(changedKeys);
1304 }
1305
1306 // remove SCDynamicStore session, release 'prefs' reference
1307 __SCPreferencesRemoveSession(prefs);
1308 }
1309
1310 ok = TRUE;
1311
1312 done :
1313
1314 pthread_mutex_unlock(&prefsPrivate->lock);
1315 return ok;
1316 }
1317
1318
1319 Boolean
1320 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs,
1321 CFRunLoopRef runLoop,
1322 CFStringRef runLoopMode)
1323 {
1324 if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) {
1325 _SCErrorSet(kSCStatusInvalidArgument);
1326 return FALSE;
1327 }
1328
1329 return __SCPreferencesScheduleWithRunLoop(prefs, runLoop, runLoopMode, NULL);
1330 }
1331
1332
1333 Boolean
1334 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs,
1335 CFRunLoopRef runLoop,
1336 CFStringRef runLoopMode)
1337 {
1338 if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) {
1339 _SCErrorSet(kSCStatusInvalidArgument);
1340 return FALSE;
1341 }
1342
1343 return __SCPreferencesUnscheduleFromRunLoop(prefs, runLoop, runLoopMode);
1344 }
1345
1346
1347 Boolean
1348 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs,
1349 dispatch_queue_t queue)
1350 {
1351 Boolean ok = FALSE;
1352
1353 if (!isA_SCPreferences(prefs)) {
1354 /* sorry, you must provide a session */
1355 _SCErrorSet(kSCStatusNoPrefsSession);
1356 return FALSE;
1357 }
1358
1359 if (queue != NULL) {
1360 ok = __SCPreferencesScheduleWithRunLoop(prefs, NULL, NULL, queue);
1361 } else {
1362 ok = __SCPreferencesUnscheduleFromRunLoop(prefs, NULL, NULL);
1363 }
1364
1365 return ok;
1366 }
1367
1368
1369 static void
1370 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs)
1371 {
1372 Boolean ok;
1373 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1374 uint32_t status = kSCStatusOK;
1375
1376 if (prefsPrivate->helper_port == MACH_PORT_NULL) {
1377 // if no helper
1378 return;
1379 }
1380
1381 // have the helper "synchronize" the prefs
1382 ok = _SCHelperExec(prefsPrivate->helper_port,
1383 SCHELPER_MSG_PREFS_SYNCHRONIZE,
1384 NULL,
1385 &status,
1386 NULL);
1387 if (!ok) {
1388 // close helper
1389 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
1390 _SCHelperClose(&prefsPrivate->helper_port);
1391 }
1392 }
1393
1394 return;
1395 }
1396
1397
1398 void
1399 SCPreferencesSynchronize(SCPreferencesRef prefs)
1400 {
1401 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1402
1403 if (!isA_SCPreferences(prefs)) {
1404 /* sorry, you must provide a session */
1405 _SCErrorSet(kSCStatusNoPrefsSession);
1406 return;
1407 }
1408
1409 SC_log(LOG_DEBUG, "SCPreferences() synchronize: %s",
1410 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
1411
1412 if (prefsPrivate->authorizationData != NULL) {
1413 __SCPreferencesSynchronize_helper(prefs);
1414 }
1415 if (prefsPrivate->prefs != NULL) {
1416 CFRelease(prefsPrivate->prefs);
1417 prefsPrivate->prefs = NULL;
1418 }
1419 if (prefsPrivate->signature != NULL) {
1420 CFRelease(prefsPrivate->signature);
1421 prefsPrivate->signature = NULL;
1422 }
1423 prefsPrivate->accessed = FALSE;
1424 prefsPrivate->changed = FALSE;
1425
1426 return;
1427 }