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