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