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