]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPOpen.c
configd-888.20.5.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCPOpen.c
1 /*
2 * Copyright(c) 2000-2016 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 <Availability.h>
38 #include <TargetConditionals.h>
39 #include <fcntl.h>
40 #include <pthread.h>
41 #include <sandbox.h>
42 #include <unistd.h>
43 #include <sys/errno.h>
44 #include <sys/cdefs.h>
45 #include <dispatch/dispatch.h>
46
47 #include "SCPreferencesInternal.h"
48 #include "SCD.h"
49 #include "SCHelper_client.h"
50 #include "dy_framework.h"
51
52
53 const AuthorizationRef kSCPreferencesUseEntitlementAuthorization = (AuthorizationRef)CFSTR("UseEntitlement");
54
55
56 __private_extern__ os_log_t
57 __log_SCPreferences()
58 {
59 static os_log_t log = NULL;
60
61 if (log == NULL) {
62 log = os_log_create("com.apple.SystemConfiguration", "SCPreferences");
63 }
64
65 return log;
66 }
67
68
69 static __inline__ CFTypeRef
70 isA_SCPreferences(CFTypeRef obj)
71 {
72 return (isA_CFType(obj, SCPreferencesGetTypeID()));
73 }
74
75
76 static CFStringRef
77 __SCPreferencesCopyDescription(CFTypeRef cf) {
78 CFAllocatorRef allocator = CFGetAllocator(cf);
79 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)cf;
80 CFMutableStringRef result;
81
82 result = CFStringCreateMutable(allocator, 0);
83 CFStringAppendFormat(result, NULL, CFSTR("<SCPreferences %p [%p]> {"), cf, allocator);
84 CFStringAppendFormat(result, NULL, CFSTR("name = %@"), prefsPrivate->name);
85 CFStringAppendFormat(result, NULL, CFSTR(", id = %@"), prefsPrivate->prefsID);
86 CFStringAppendFormat(result, NULL, CFSTR(", path = %s"),
87 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
88 if (prefsPrivate->accessed) {
89 CFStringAppendFormat(result, NULL, CFSTR(", accessed"));
90 }
91 if (prefsPrivate->changed) {
92 CFStringAppendFormat(result, NULL, CFSTR(", changed"));
93 }
94 if (prefsPrivate->locked) {
95 CFStringAppendFormat(result, NULL, CFSTR(", locked"));
96 }
97 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
98 CFStringAppendFormat(result, NULL, CFSTR(", helper port = 0x%x"), prefsPrivate->helper_port);
99 }
100 CFStringAppendFormat(result, NULL, CFSTR("}"));
101
102 return result;
103 }
104
105
106 static void
107 __SCPreferencesDeallocate(CFTypeRef cf)
108 {
109 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)cf;
110
111 /* release resources */
112
113 pthread_mutex_destroy(&prefsPrivate->lock);
114
115 if (prefsPrivate->name) CFRelease(prefsPrivate->name);
116 if (prefsPrivate->prefsID) CFRelease(prefsPrivate->prefsID);
117 if (prefsPrivate->options) CFRelease(prefsPrivate->options);
118 if (prefsPrivate->path) CFAllocatorDeallocate(NULL, prefsPrivate->path);
119 if (prefsPrivate->newPath) CFAllocatorDeallocate(NULL, prefsPrivate->newPath);
120 if (prefsPrivate->lockFD != -1) {
121 if (prefsPrivate->lockPath != NULL) {
122 unlink(prefsPrivate->lockPath);
123 }
124 close(prefsPrivate->lockFD);
125 }
126 if (prefsPrivate->lockPath) CFAllocatorDeallocate(NULL, prefsPrivate->lockPath);
127 if (prefsPrivate->signature) CFRelease(prefsPrivate->signature);
128 if (prefsPrivate->sessionNoO_EXLOCK != NULL) {
129 CFRelease(prefsPrivate->sessionNoO_EXLOCK);
130 }
131 if (prefsPrivate->sessionKeyLock) CFRelease(prefsPrivate->sessionKeyLock);
132 if (prefsPrivate->sessionKeyCommit) CFRelease(prefsPrivate->sessionKeyCommit);
133 if (prefsPrivate->sessionKeyApply) CFRelease(prefsPrivate->sessionKeyApply);
134 if (prefsPrivate->rlsContext.release != NULL) {
135 (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info);
136 }
137 if (prefsPrivate->prefs) CFRelease(prefsPrivate->prefs);
138 if (prefsPrivate->authorizationData != NULL) CFRelease(prefsPrivate->authorizationData);
139 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
140 (void) _SCHelperExec(prefsPrivate->helper_port,
141 SCHELPER_MSG_PREFS_CLOSE,
142 NULL,
143 NULL,
144 NULL);
145 _SCHelperClose(&prefsPrivate->helper_port);
146 }
147
148 return;
149 }
150
151
152 static CFTypeID __kSCPreferencesTypeID = _kCFRuntimeNotATypeID;
153
154
155 static const CFRuntimeClass __SCPreferencesClass = {
156 0, // version
157 "SCPreferences", // className
158 NULL, // init
159 NULL, // copy
160 __SCPreferencesDeallocate, // dealloc
161 NULL, // equal
162 NULL, // hash
163 NULL, // copyFormattingDesc
164 __SCPreferencesCopyDescription // copyDebugDesc
165 };
166
167
168 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
169
170 static void
171 __SCPreferencesInitialize(void) {
172 /* register with CoreFoundation */
173 __kSCPreferencesTypeID = _CFRuntimeRegisterClass(&__SCPreferencesClass);
174 return;
175 }
176
177
178 static SCPreferencesPrivateRef
179 __SCPreferencesCreatePrivate(CFAllocatorRef allocator)
180 {
181 SCPreferencesPrivateRef prefsPrivate;
182 uint32_t size;
183
184 /* initialize runtime */
185 pthread_once(&initialized, __SCPreferencesInitialize);
186
187 /* allocate prefs session */
188 size = sizeof(SCPreferencesPrivate) - sizeof(CFRuntimeBase);
189 prefsPrivate = (SCPreferencesPrivateRef)_CFRuntimeCreateInstance(allocator,
190 __kSCPreferencesTypeID,
191 size,
192 NULL);
193 if (prefsPrivate == NULL) {
194 return NULL;
195 }
196
197 /* initialize non-zero/NULL members */
198 pthread_mutex_init(&prefsPrivate->lock, NULL);
199 prefsPrivate->lockFD = -1;
200 prefsPrivate->isRoot = (geteuid() == 0);
201
202 return prefsPrivate;
203 }
204
205
206 __private_extern__ Boolean
207 __SCPreferencesCreate_helper(SCPreferencesRef prefs)
208 {
209 CFDataRef data = NULL;
210 CFMutableDictionaryRef info;
211 CFNumberRef num;
212 Boolean ok;
213 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
214 uint32_t status = kSCStatusOK;
215 CFStringRef str;
216 uint32_t pid = getpid();
217
218 // start helper
219 ok = _SCHelperOpen(prefsPrivate->authorizationData,
220 &prefsPrivate->helper_port);
221 if (!ok) {
222 goto fail;
223 }
224
225 // create a dictionary of information to pass to the helper
226 info = CFDictionaryCreateMutable(NULL,
227 0,
228 &kCFTypeDictionaryKeyCallBacks,
229 &kCFTypeDictionaryValueCallBacks);
230
231 // save prefsID
232 if (prefsPrivate->prefsID != NULL) {
233 CFDictionarySetValue(info, CFSTR("prefsID"), prefsPrivate->prefsID);
234 }
235
236 // save options
237 if (prefsPrivate->options != NULL) {
238 CFDictionarySetValue(info, CFSTR("options"), prefsPrivate->options);
239 }
240
241 // save preferences session "name"
242 CFDictionarySetValue(info, CFSTR("name"), prefsPrivate->name);
243
244 // save PID
245 num = CFNumberCreate(NULL, kCFNumberSInt32Type, &pid);
246 CFDictionarySetValue(info, CFSTR("PID"), num);
247 CFRelease(num);
248
249 // save process name
250 str = CFStringCreateWithCString(NULL, getprogname(), kCFStringEncodingUTF8);
251 CFDictionarySetValue(info, CFSTR("PROC_NAME"), str);
252 CFRelease(str);
253
254 // serialize the info
255 ok = _SCSerialize(info, &data, NULL, NULL);
256 CFRelease(info);
257 if (data == NULL || !ok) {
258 goto fail;
259 }
260
261 // have the helper "open" the prefs
262 ok = _SCHelperExec(prefsPrivate->helper_port,
263 SCHELPER_MSG_PREFS_OPEN,
264 data,
265 &status,
266 NULL);
267 if (data != NULL) CFRelease(data);
268 if (!ok) {
269 goto fail;
270 }
271
272 if (status != kSCStatusOK) {
273 goto error;
274 }
275
276 return TRUE;
277
278 fail :
279
280 // close helper
281 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
282 _SCHelperClose(&prefsPrivate->helper_port);
283 }
284
285 status = kSCStatusAccessError;
286
287 error :
288
289 // return error
290 _SCErrorSet(status);
291 return FALSE;
292 }
293
294
295 static Boolean
296 __SCPreferencesAccess_helper(SCPreferencesRef prefs)
297 {
298 Boolean ok;
299 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
300 CFDictionaryRef serverDict = NULL;
301 CFDictionaryRef serverPrefs = NULL;
302 CFDictionaryRef serverSignature = NULL;
303 uint32_t status = kSCStatusOK;
304 CFDataRef reply = NULL;
305
306 if (prefsPrivate->helper_port == MACH_PORT_NULL) {
307 ok = __SCPreferencesCreate_helper(prefs);
308 if (!ok) {
309 return FALSE;
310 }
311 }
312
313 // have the helper "access" the prefs
314 ok = _SCHelperExec(prefsPrivate->helper_port,
315 SCHELPER_MSG_PREFS_ACCESS,
316 NULL,
317 &status,
318 &reply);
319 if (!ok) {
320 goto fail;
321 }
322
323 if (status != kSCStatusOK) {
324 goto error;
325 }
326
327 if (reply == NULL) {
328 goto fail;
329 }
330
331 ok = _SCUnserialize((CFPropertyListRef *)&serverDict, reply, NULL, 0);
332 CFRelease(reply);
333 if (!ok) {
334 goto fail;
335 }
336
337 if (isA_CFDictionary(serverDict)) {
338 serverPrefs = CFDictionaryGetValue(serverDict, CFSTR("preferences"));
339 serverPrefs = isA_CFDictionary(serverPrefs);
340
341 serverSignature = CFDictionaryGetValue(serverDict, CFSTR("signature"));
342 serverSignature = isA_CFData(serverSignature);
343 }
344
345 if ((serverPrefs == NULL) || (serverSignature == NULL)) {
346 if (serverDict != NULL) CFRelease(serverDict);
347 goto fail;
348 }
349
350 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, serverPrefs);
351 prefsPrivate->signature = CFRetain(serverSignature);
352 prefsPrivate->accessed = TRUE;
353 CFRelease(serverDict);
354
355 return TRUE;
356
357 fail :
358
359 // close helper
360 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
361 _SCHelperClose(&prefsPrivate->helper_port);
362 }
363
364 status = kSCStatusAccessError;
365
366 error :
367
368 // return error
369 _SCErrorSet(status);
370 return FALSE;
371 }
372
373
374 static SCPreferencesPrivateRef
375 __SCPreferencesCreate(CFAllocatorRef allocator,
376 CFStringRef name,
377 CFStringRef prefsID,
378 CFDataRef authorizationData,
379 CFDictionaryRef options)
380 {
381 SCPreferencesPrivateRef prefsPrivate;
382 int sc_status = kSCStatusOK;
383
384 /*
385 * allocate and initialize a new prefs session
386 */
387 prefsPrivate = __SCPreferencesCreatePrivate(allocator);
388 if (prefsPrivate == NULL) {
389 return NULL;
390 }
391
392 prefsPrivate->name = CFStringCreateCopy(allocator, name);
393 if (prefsID != NULL) {
394 prefsPrivate->prefsID = CFStringCreateCopy(allocator, prefsID);
395 }
396 if (authorizationData != NULL) {
397 prefsPrivate->authorizationData = CFRetain(authorizationData);
398 }
399 if (options != NULL) {
400 prefsPrivate->options = CFDictionaryCreateCopy(allocator, options);
401 }
402
403 retry :
404
405 /*
406 * convert prefsID to path
407 */
408 prefsPrivate->path = __SCPreferencesPath(allocator,
409 prefsID,
410 (prefsPrivate->newPath == NULL));
411 if (prefsPrivate->path == NULL) {
412 sc_status = kSCStatusFailed;
413 goto error;
414 }
415
416 if (access(prefsPrivate->path, R_OK) == 0) {
417 goto done;
418 }
419
420 switch (errno) {
421 case ENOENT :
422 /* no prefs file */
423 if ((prefsID == NULL) || !CFStringHasPrefix(prefsID, CFSTR("/"))) {
424 /* if default preference ID or relative path */
425 if (prefsPrivate->newPath == NULL) {
426 /*
427 * we've looked in the "new" prefs directory
428 * without success. Save the "new" path and
429 * look in the "old" prefs directory.
430 */
431 prefsPrivate->newPath = prefsPrivate->path;
432 goto retry;
433 } else {
434 /*
435 * we've looked in both the "new" and "old"
436 * prefs directories without success. Use
437 * the "new" path.
438 */
439 CFAllocatorDeallocate(NULL, prefsPrivate->path);
440 prefsPrivate->path = prefsPrivate->newPath;
441 prefsPrivate->newPath = NULL;
442 }
443 }
444
445 /* no preference data, start fresh */
446 sc_status = kSCStatusNoConfigFile;
447 goto done;
448 case EPERM :
449 case EACCES :
450 if (prefsPrivate->authorizationData != NULL) {
451 /* no problem, we'll be using the helper */
452 goto done;
453 }
454
455 SC_log(LOG_INFO, "open() failed: %s", strerror(errno));
456 sc_status = kSCStatusAccessError;
457 break;
458 default :
459 SC_log(LOG_INFO, "open() failed: %s", strerror(errno));
460 sc_status = kSCStatusFailed;
461 break;
462 }
463
464 error:
465
466 CFRelease(prefsPrivate);
467 _SCErrorSet(sc_status);
468 return NULL;
469
470 done :
471
472 /* all OK */
473 _SCErrorSet(sc_status);
474 return prefsPrivate;
475 }
476
477
478 __private_extern__ void
479 __SCPreferencesAccess(SCPreferencesRef prefs)
480 {
481 CFAllocatorRef allocator = CFGetAllocator(prefs);
482 int fd = -1;
483 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
484 struct stat statBuf;
485
486 if (prefsPrivate->accessed) {
487 // if preference data has already been accessed
488 return;
489 }
490
491 if (access(prefsPrivate->path, R_OK) == 0) {
492 fd = open(prefsPrivate->path, O_RDONLY, 0644);
493 } else {
494 fd = -1;
495 }
496 if (fd != -1) {
497 // create signature
498 if (fstat(fd, &statBuf) == -1) {
499 SC_log(LOG_NOTICE, "fstat() failed: %s", strerror(errno));
500 bzero(&statBuf, sizeof(statBuf));
501 }
502 } else {
503 switch (errno) {
504 case ENOENT :
505 /* no preference data, start fresh */
506 break;
507 case EPERM :
508 case EACCES :
509 if (prefsPrivate->authorizationData != NULL) {
510 if (__SCPreferencesAccess_helper(prefs)) {
511 goto done;
512 } else {
513 SC_log(LOG_NOTICE, "__SCPreferencesAccess_helper() failed: %s",
514 SCErrorString(SCError()));
515 }
516 break;
517 }
518 // fall through
519 default :
520 SC_log(LOG_NOTICE, "open() failed: %s", strerror(errno));
521 break;
522 }
523 bzero(&statBuf, sizeof(statBuf));
524 }
525
526 if (prefsPrivate->signature != NULL) CFRelease(prefsPrivate->signature);
527 prefsPrivate->signature = __SCPSignatureFromStatbuf(&statBuf);
528
529 if (statBuf.st_size > 0) {
530 CFDictionaryRef dict;
531 CFErrorRef error = NULL;
532 CFMutableDataRef xmlData;
533
534 /*
535 * extract property list
536 */
537 xmlData = CFDataCreateMutable(allocator, (CFIndex)statBuf.st_size);
538 CFDataSetLength(xmlData, (CFIndex)statBuf.st_size);
539 if (read(fd, (void *)CFDataGetBytePtr(xmlData), (CFIndex)statBuf.st_size) != (CFIndex)statBuf.st_size) {
540 /* corrupt prefs file, start fresh */
541 SC_log(LOG_INFO, "read(): could not load preference data");
542 CFRelease(xmlData);
543 xmlData = NULL;
544 goto done;
545 }
546
547 /*
548 * load preferences
549 */
550 dict = CFPropertyListCreateWithData(allocator, xmlData, kCFPropertyListImmutable, NULL, &error);
551 CFRelease(xmlData);
552 if (dict == NULL) {
553 /* corrupt prefs file, start fresh */
554 if (error != NULL) {
555 SC_log(LOG_NOTICE, "CFPropertyListCreateWithData(): %@", error);
556 CFRelease(error);
557 }
558 goto done;
559 }
560
561 /*
562 * make sure that we've got a dictionary
563 */
564 if (!isA_CFDictionary(dict)) {
565 /* corrupt prefs file, start fresh */
566 SC_log(LOG_INFO, "CFGetTypeID(): not a dictionary");
567 CFRelease(dict);
568 goto done;
569 }
570
571 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(allocator, 0, dict);
572 CFRelease(dict);
573 }
574
575 done :
576
577 if (fd != -1) {
578 (void) close(fd);
579 }
580
581 if (prefsPrivate->prefs == NULL) {
582 /*
583 * new file, create empty preferences
584 */
585 // SC_log(LOG_INFO, "creating new preferences file");
586 prefsPrivate->prefs = CFDictionaryCreateMutable(allocator,
587 0,
588 &kCFTypeDictionaryKeyCallBacks,
589 &kCFTypeDictionaryValueCallBacks);
590 prefsPrivate->changed = TRUE;
591 }
592
593 SC_log(LOG_DEBUG, "SCPreferences() access: %s",
594 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
595
596 prefsPrivate->accessed = TRUE;
597 return;
598 }
599
600
601 SCPreferencesRef
602 SCPreferencesCreate(CFAllocatorRef allocator,
603 CFStringRef name,
604 CFStringRef prefsID)
605 {
606 SCPreferencesPrivateRef prefsPrivate;
607
608 prefsPrivate = __SCPreferencesCreate(allocator, name, prefsID, NULL, NULL);
609 return (SCPreferencesRef)prefsPrivate;
610 }
611
612
613 SCPreferencesRef
614 SCPreferencesCreateWithAuthorization(CFAllocatorRef allocator,
615 CFStringRef name,
616 CFStringRef prefsID,
617 AuthorizationRef authorization)
618 {
619 SCPreferencesRef prefs;
620
621 #if !TARGET_OS_IPHONE
622 if (authorization == NULL) {
623 authorization = kSCPreferencesUseEntitlementAuthorization;
624 }
625 #else // !TARGET_OS_IPHONE
626 authorization = kSCPreferencesUseEntitlementAuthorization;
627 #endif // !TARGET_OS_IPHONE
628
629 prefs = SCPreferencesCreateWithOptions(allocator, name, prefsID, authorization, NULL);
630 return prefs;
631 }
632
633
634 SCPreferencesRef
635 SCPreferencesCreateWithOptions(CFAllocatorRef allocator,
636 CFStringRef name,
637 CFStringRef prefsID,
638 AuthorizationRef authorization,
639 CFDictionaryRef options)
640 {
641 CFDataRef authorizationData = NULL;
642 SCPreferencesPrivateRef prefsPrivate;
643
644 if (options != NULL) {
645 if (!isA_CFDictionary(options)) {
646 _SCErrorSet(kSCStatusInvalidArgument);
647 return NULL;
648 }
649 }
650
651 if (authorization != NULL) {
652 CFMutableDictionaryRef authorizationDict;
653 CFBundleRef bundle;
654 CFStringRef bundleID = NULL;
655
656 authorizationDict = CFDictionaryCreateMutable(NULL,
657 0,
658 &kCFTypeDictionaryKeyCallBacks,
659 &kCFTypeDictionaryValueCallBacks);
660 #if !TARGET_OS_IPHONE
661 if (authorization != kSCPreferencesUseEntitlementAuthorization) {
662 CFDataRef data;
663 AuthorizationExternalForm extForm;
664 OSStatus os_status;
665
666 os_status = AuthorizationMakeExternalForm(authorization, &extForm);
667 if (os_status != errAuthorizationSuccess) {
668 SC_log(LOG_INFO, "AuthorizationMakeExternalForm() failed");
669 _SCErrorSet(kSCStatusInvalidArgument);
670 CFRelease(authorizationDict);
671 return NULL;
672 }
673
674 data = CFDataCreate(NULL, (const UInt8 *)extForm.bytes, sizeof(extForm.bytes));
675 CFDictionaryAddValue(authorizationDict,
676 kSCHelperAuthAuthorization,
677 data);
678 CFRelease(data);
679 }
680 #endif
681
682 /* get the application/executable/bundle name */
683 bundle = CFBundleGetMainBundle();
684 if (bundle != NULL) {
685 bundleID = CFBundleGetIdentifier(bundle);
686 if (bundleID != NULL) {
687 CFRetain(bundleID);
688 } else {
689 CFURLRef url;
690
691 url = CFBundleCopyExecutableURL(bundle);
692 if (url != NULL) {
693 bundleID = CFURLCopyPath(url);
694 CFRelease(url);
695 }
696 }
697
698 if (bundleID != NULL) {
699 if (CFEqual(bundleID, CFSTR("/"))) {
700 CFRelease(bundleID);
701 bundleID = NULL;
702 }
703 }
704 }
705 if (bundleID == NULL) {
706 bundleID = CFStringCreateWithFormat(NULL, NULL, CFSTR("Unknown(%d)"), getpid());
707 }
708 CFDictionaryAddValue(authorizationDict,
709 kSCHelperAuthCallerInfo,
710 bundleID);
711 CFRelease(bundleID);
712
713 if (authorizationDict != NULL) {
714 (void) _SCSerialize((CFPropertyListRef)authorizationDict,
715 &authorizationData,
716 NULL,
717 NULL);
718 CFRelease(authorizationDict);
719 }
720 }
721
722 prefsPrivate = __SCPreferencesCreate(allocator, name, prefsID, authorizationData, options);
723 if (authorizationData != NULL) CFRelease(authorizationData);
724
725 return (SCPreferencesRef)prefsPrivate;
726 }
727
728
729 CFTypeID
730 SCPreferencesGetTypeID(void) {
731 pthread_once(&initialized, __SCPreferencesInitialize); /* initialize runtime */
732 return __kSCPreferencesTypeID;
733 }
734
735
736 static void
737 prefsNotify(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
738 {
739 void *context_info;
740 void (*context_release)(const void *);
741 CFIndex i;
742 CFIndex n;
743 SCPreferencesNotification notify = 0;
744 SCPreferencesRef prefs = (SCPreferencesRef)info;
745 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
746 SCPreferencesCallBack rlsFunction;
747
748 n = (changedKeys != NULL) ? CFArrayGetCount(changedKeys) : 0;
749 for (i = 0; i < n; i++) {
750 CFStringRef key;
751
752 key = CFArrayGetValueAtIndex(changedKeys, i);
753 if (CFEqual(key, prefsPrivate->sessionKeyCommit)) {
754 // if preferences have been saved
755 notify |= kSCPreferencesNotificationCommit;
756 }
757 if (CFEqual(key, prefsPrivate->sessionKeyApply)) {
758 // if stored preferences should be applied to current configuration
759 notify |= kSCPreferencesNotificationApply;
760 }
761 }
762
763 if (notify == 0) {
764 // if no changes
765 return;
766 }
767
768 pthread_mutex_lock(&prefsPrivate->lock);
769
770 /* callout */
771 rlsFunction = prefsPrivate->rlsFunction;
772 if (prefsPrivate->rlsContext.retain != NULL) {
773 context_info = (void *)prefsPrivate->rlsContext.retain(prefsPrivate->rlsContext.info);
774 context_release = prefsPrivate->rlsContext.release;
775 } else {
776 context_info = prefsPrivate->rlsContext.info;
777 context_release = NULL;
778 }
779
780 pthread_mutex_unlock(&prefsPrivate->lock);
781
782 if (rlsFunction != NULL) {
783 SC_log(LOG_DEBUG, "exec SCPreferences callout: %s%s%s",
784 ((notify & kSCPreferencesNotificationCommit) != 0) ? "commit" : "",
785 ((notify & kSCPreferencesNotificationCommit|kSCPreferencesNotificationApply) != 0) ? ", " : "",
786 ((notify & kSCPreferencesNotificationApply) != 0) ? "apply" : "");
787 (*rlsFunction)(prefs, notify, context_info);
788 }
789
790 if (context_release != NULL) {
791 (*context_release)(context_info);
792 }
793
794 return;
795 }
796
797
798 __private_extern__ void
799 __SCPreferencesAddSessionKeys(SCPreferencesRef prefs)
800 {
801 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
802
803 /* create the session "commit" key */
804 if (prefsPrivate->sessionKeyCommit == NULL) {
805 prefsPrivate->sessionKeyCommit = _SCPNotificationKey(NULL,
806 prefsPrivate->prefsID,
807 kSCPreferencesKeyCommit);
808 }
809
810 /* create the session "apply" key */
811 if (prefsPrivate->sessionKeyApply == NULL) {
812 prefsPrivate->sessionKeyApply = _SCPNotificationKey(NULL,
813 prefsPrivate->prefsID,
814 kSCPreferencesKeyApply);
815 }
816
817 return;
818 }
819
820
821 __private_extern__ Boolean
822 __SCPreferencesAddSession(SCPreferencesRef prefs)
823 {
824 CFAllocatorRef allocator = CFGetAllocator(prefs);
825 SCDynamicStoreContext context = { 0
826 , (void *)prefs
827 , CFRetain
828 , CFRelease
829 , CFCopyDescription
830 };
831 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
832
833 if (prefsPrivate->sessionRefcnt == 0) {
834 /* establish a dynamic store session */
835 prefsPrivate->session = SCDynamicStoreCreate(allocator,
836 prefsPrivate->name,
837 prefsNotify,
838 &context);
839 if (prefsPrivate->session == NULL) {
840 SC_log(LOG_INFO, "SCDynamicStoreCreate() failed");
841 return FALSE;
842 }
843 }
844
845 prefsPrivate->sessionRefcnt++;
846 return TRUE;
847 }
848
849
850 __private_extern__ void
851 __SCPreferencesRemoveSession(SCPreferencesRef prefs)
852 {
853 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
854
855 if (prefsPrivate->sessionRefcnt > 0) {
856 if (--prefsPrivate->sessionRefcnt == 0) {
857 CFRelease(prefsPrivate->session);
858 prefsPrivate->session = NULL;
859 }
860 }
861
862 return;
863 }
864
865
866 Boolean
867 SCPreferencesSetCallback(SCPreferencesRef prefs,
868 SCPreferencesCallBack callout,
869 SCPreferencesContext *context)
870 {
871 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
872
873 if (!isA_SCPreferences(prefs)) {
874 /* sorry, you must provide a session */
875 _SCErrorSet(kSCStatusNoPrefsSession);
876 return FALSE;
877 }
878
879 pthread_mutex_lock(&prefsPrivate->lock);
880
881 if (prefsPrivate->rlsContext.release != NULL) {
882 /* let go of the current context */
883 (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info);
884 }
885
886 prefsPrivate->rlsFunction = callout;
887 prefsPrivate->rlsContext.info = NULL;
888 prefsPrivate->rlsContext.retain = NULL;
889 prefsPrivate->rlsContext.release = NULL;
890 prefsPrivate->rlsContext.copyDescription = NULL;
891 if (context != NULL) {
892 bcopy(context, &prefsPrivate->rlsContext, sizeof(SCPreferencesContext));
893 if (context->retain != NULL) {
894 prefsPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
895 }
896 }
897
898 pthread_mutex_unlock(&prefsPrivate->lock);
899
900 return TRUE;
901 }
902
903
904 static Boolean
905 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs,
906 CFRunLoopRef runLoop,
907 CFStringRef runLoopMode,
908 dispatch_queue_t queue)
909 {
910 Boolean ok = FALSE;
911 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
912
913 pthread_mutex_lock(&prefsPrivate->lock);
914
915 if ((prefsPrivate->dispatchQueue != NULL) || // if we are already scheduled on a dispatch queue
916 ((queue != NULL) && prefsPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
917 _SCErrorSet(kSCStatusInvalidArgument);
918 goto done;
919 }
920
921 if (!prefsPrivate->scheduled) {
922 CFMutableArrayRef keys;
923
924 // add SCDynamicStore session (for notifications) ... and hold a 'prefs' reference
925 if (prefsPrivate->session == NULL) {
926 ok = __SCPreferencesAddSession(prefs);
927 if (!ok) {
928 goto done;
929 }
930 assert(prefsPrivate->session != NULL);
931 }
932
933 // add SCDynamicStore "keys"
934 __SCPreferencesAddSessionKeys(prefs);
935
936 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
937 CFArrayAppendValue(keys, prefsPrivate->sessionKeyCommit);
938 CFArrayAppendValue(keys, prefsPrivate->sessionKeyApply);
939 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, keys, NULL);
940 CFRelease(keys);
941
942 if (runLoop != NULL) {
943 prefsPrivate->rls = SCDynamicStoreCreateRunLoopSource(NULL, prefsPrivate->session, 0);
944 prefsPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
945 }
946
947 prefsPrivate->scheduled = TRUE;
948 }
949
950 if (queue != NULL) {
951 ok = SCDynamicStoreSetDispatchQueue(prefsPrivate->session, queue);
952 if (!ok) {
953 prefsPrivate->scheduled = FALSE;
954 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL);
955 __SCPreferencesRemoveSession(prefs);
956 goto done;
957 }
958
959 prefsPrivate->dispatchQueue = queue;
960 dispatch_retain(prefsPrivate->dispatchQueue);
961 } else {
962 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) {
963 /*
964 * if we do not already have notifications scheduled with
965 * this runLoop / runLoopMode
966 */
967 CFRunLoopAddSource(runLoop, prefsPrivate->rls, runLoopMode);
968 }
969
970 _SC_schedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList);
971 }
972
973 ok = TRUE;
974
975 done :
976
977 pthread_mutex_unlock(&prefsPrivate->lock);
978 return ok;
979 }
980
981
982 static Boolean
983 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs,
984 CFRunLoopRef runLoop,
985 CFStringRef runLoopMode)
986 {
987 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
988 CFIndex n = 0;
989 Boolean ok = FALSE;
990
991 pthread_mutex_lock(&prefsPrivate->lock);
992
993 if ((runLoop != NULL) && !prefsPrivate->scheduled) { // if we should be scheduled (but are not)
994 _SCErrorSet(kSCStatusInvalidArgument);
995 goto done;
996 }
997
998 if (((runLoop == NULL) && (prefsPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
999 ((runLoop != NULL) && (prefsPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1000 _SCErrorSet(kSCStatusInvalidArgument);
1001 goto done;
1002 }
1003
1004 if (runLoop == NULL) {
1005 SCDynamicStoreSetDispatchQueue(prefsPrivate->session, NULL);
1006 dispatch_release(prefsPrivate->dispatchQueue);
1007 prefsPrivate->dispatchQueue = NULL;
1008 } else {
1009 if (!_SC_unschedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList, FALSE)) {
1010 // if not currently scheduled on this runLoop / runLoopMode
1011 _SCErrorSet(kSCStatusInvalidArgument);
1012 goto done;
1013 }
1014
1015 n = CFArrayGetCount(prefsPrivate->rlList);
1016 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) {
1017 /*
1018 * if we are no longer scheduled to receive notifications for
1019 * this runLoop / runLoopMode
1020 */
1021 CFRunLoopRemoveSource(runLoop, prefsPrivate->rls, runLoopMode);
1022
1023 if (n == 0) {
1024 // if *all* notifications have been unscheduled
1025 CFRelease(prefsPrivate->rlList);
1026 prefsPrivate->rlList = NULL;
1027 CFRunLoopSourceInvalidate(prefsPrivate->rls);
1028 CFRelease(prefsPrivate->rls);
1029 prefsPrivate->rls = NULL;
1030 }
1031 }
1032 }
1033
1034 if (n == 0) {
1035 CFArrayRef changedKeys;
1036
1037 // if *all* notifications have been unscheduled
1038 prefsPrivate->scheduled = FALSE;
1039
1040 // no need to track changes
1041 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL);
1042
1043 // clear out any pending notifications
1044 changedKeys = SCDynamicStoreCopyNotifiedKeys(prefsPrivate->session);
1045 if (changedKeys != NULL) {
1046 CFRelease(changedKeys);
1047 }
1048
1049 // remove SCDynamicStore session, release 'prefs' reference
1050 __SCPreferencesRemoveSession(prefs);
1051 }
1052
1053 ok = TRUE;
1054
1055 done :
1056
1057 pthread_mutex_unlock(&prefsPrivate->lock);
1058 return ok;
1059 }
1060
1061
1062 Boolean
1063 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs,
1064 CFRunLoopRef runLoop,
1065 CFStringRef runLoopMode)
1066 {
1067 if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) {
1068 _SCErrorSet(kSCStatusInvalidArgument);
1069 return FALSE;
1070 }
1071
1072 return __SCPreferencesScheduleWithRunLoop(prefs, runLoop, runLoopMode, NULL);
1073 }
1074
1075
1076 Boolean
1077 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs,
1078 CFRunLoopRef runLoop,
1079 CFStringRef runLoopMode)
1080 {
1081 if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) {
1082 _SCErrorSet(kSCStatusInvalidArgument);
1083 return FALSE;
1084 }
1085
1086 return __SCPreferencesUnscheduleFromRunLoop(prefs, runLoop, runLoopMode);
1087 }
1088
1089
1090 Boolean
1091 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs,
1092 dispatch_queue_t queue)
1093 {
1094 Boolean ok = FALSE;
1095
1096 if (!isA_SCPreferences(prefs)) {
1097 /* sorry, you must provide a session */
1098 _SCErrorSet(kSCStatusNoPrefsSession);
1099 return FALSE;
1100 }
1101
1102 if (queue != NULL) {
1103 ok = __SCPreferencesScheduleWithRunLoop(prefs, NULL, NULL, queue);
1104 } else {
1105 ok = __SCPreferencesUnscheduleFromRunLoop(prefs, NULL, NULL);
1106 }
1107
1108 return ok;
1109 }
1110
1111
1112 static void
1113 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs)
1114 {
1115 Boolean ok;
1116 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1117 uint32_t status = kSCStatusOK;
1118
1119 if (prefsPrivate->helper_port == MACH_PORT_NULL) {
1120 // if no helper
1121 return;
1122 }
1123
1124 // have the helper "synchronize" the prefs
1125 ok = _SCHelperExec(prefsPrivate->helper_port,
1126 SCHELPER_MSG_PREFS_SYNCHRONIZE,
1127 NULL,
1128 &status,
1129 NULL);
1130 if (!ok) {
1131 // close helper
1132 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
1133 _SCHelperClose(&prefsPrivate->helper_port);
1134 }
1135 }
1136
1137 return;
1138 }
1139
1140
1141 void
1142 SCPreferencesSynchronize(SCPreferencesRef prefs)
1143 {
1144 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1145
1146 if (!isA_SCPreferences(prefs)) {
1147 /* sorry, you must provide a session */
1148 _SCErrorSet(kSCStatusNoPrefsSession);
1149 return;
1150 }
1151
1152 SC_log(LOG_DEBUG, "SCPreferences() synchronize: %s",
1153 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
1154
1155 if (prefsPrivate->authorizationData != NULL) {
1156 __SCPreferencesSynchronize_helper(prefs);
1157 }
1158 if (prefsPrivate->prefs != NULL) {
1159 CFRelease(prefsPrivate->prefs);
1160 prefsPrivate->prefs = NULL;
1161 }
1162 if (prefsPrivate->signature != NULL) {
1163 CFRelease(prefsPrivate->signature);
1164 prefsPrivate->signature = NULL;
1165 }
1166 prefsPrivate->accessed = FALSE;
1167 prefsPrivate->changed = FALSE;
1168
1169 return;
1170 }