1/* 2 * Copyright (c) 2014 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/* CFUtilities.c 25 Copyright (c) 1998-2013, Apple Inc. All rights reserved. 26 Responsibility: Tony Parker 27*/ 28 29#include <CoreFoundation/CFPriv.h> 30#include "CFInternal.h" 31#include "CFLocaleInternal.h" 32#include <CoreFoundation/CFPriv.h> 33#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS 34#include <CoreFoundation/CFBundle.h> 35#endif 36#include <CoreFoundation/CFURLAccess.h> 37#include <CoreFoundation/CFPropertyList.h> 38#include <CoreFoundation/CFTimeZone.h> 39#include <CoreFoundation/CFCalendar.h> 40#if DEPLOYMENT_TARGET_WINDOWS 41#include <process.h> 42#endif 43#include <math.h> 44#include <string.h> 45#include <stdio.h> 46#include <stdlib.h> 47#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS 48#include <asl.h> 49#else 50#define ASL_LEVEL_EMERG 0 51#define ASL_LEVEL_DEBUG 7 52#endif 53 54#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 55#include <unistd.h> 56#include <sys/uio.h> 57#include <mach/mach.h> 58#include <pthread.h> 59#include <mach-o/loader.h> 60#include <mach-o/dyld.h> 61#include <crt_externs.h> 62#include <dlfcn.h> 63#include <vproc.h> 64#include <vproc_priv.h> 65#include <sys/sysctl.h> 66#include <sys/stat.h> 67#include <mach/mach.h> 68#include <mach/mach_vm.h> 69#include <sys/mman.h> 70#include <stdio.h> 71#include <sys/errno.h> 72#include <mach/mach_time.h> 73#include <Block.h> 74#endif 75#if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD 76#include <string.h> 77#include <pthread.h> 78#include <sys/mman.h> 79#endif 80 81/* Comparator is passed the address of the values. */ 82/* Binary searches a sorted-increasing array of some type. 83 Return value is either 1) the index of the element desired, 84 if the target value exists in the list, 2) greater than or 85 equal to count, if the element is greater than all the values 86 in the list, or 3) the index of the element greater than the 87 target value. 88 89 For example, a search in the list of integers: 90 2 3 5 7 11 13 17 91 92 For... Will Return... 93 2 0 94 5 2 95 23 7 96 1 0 97 9 4 98 99 For instance, if you just care about found/not found: 100 index = CFBSearch(list, count, elem); 101 if (count <= index || list[index] != elem) { 102 * Not found * 103 } else { 104 * Found * 105 } 106 107*/ 108CF_PRIVATE CFIndex CFBSearch(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) { 109 const char *ptr = (const char *)list; 110 while (0 < count) { 111 CFIndex half = count / 2; 112 const char *probe = ptr + elementSize * half; 113 CFComparisonResult cr = comparator(element, probe, context); 114 if (0 == cr) return (probe - (const char *)list) / elementSize; 115 ptr = (cr < 0) ? ptr : probe + elementSize; 116 count = (cr < 0) ? half : (half + (count & 1) - 1); 117 } 118 return (ptr - (const char *)list) / elementSize; 119} 120 121 122#define ELF_STEP(B) T1 = (H << 4) + B; T2 = T1 & 0xF0000000; if (T2) T1 ^= (T2 >> 24); T1 &= (~T2); H = T1; 123 124CFHashCode CFHashBytes(uint8_t *bytes, CFIndex length) { 125 /* The ELF hash algorithm, used in the ELF object file format */ 126 UInt32 H = 0, T1, T2; 127 SInt32 rem = length; 128 while (3 < rem) { 129 ELF_STEP(bytes[length - rem]); 130 ELF_STEP(bytes[length - rem + 1]); 131 ELF_STEP(bytes[length - rem + 2]); 132 ELF_STEP(bytes[length - rem + 3]); 133 rem -= 4; 134 } 135 switch (rem) { 136 case 3: ELF_STEP(bytes[length - 3]); 137 case 2: ELF_STEP(bytes[length - 2]); 138 case 1: ELF_STEP(bytes[length - 1]); 139 case 0: ; 140 } 141 return H; 142} 143 144#undef ELF_STEP 145 146 147#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 148CF_PRIVATE uintptr_t __CFFindPointer(uintptr_t ptr, uintptr_t start) { 149 vm_map_t task = mach_task_self(); 150 mach_vm_address_t address = start; 151 for (;;) { 152 mach_vm_size_t size = 0; 153 vm_region_basic_info_data_64_t info; 154 mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; 155 mach_port_t object_name; 156 kern_return_t ret = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &count, &object_name); 157 if (KERN_SUCCESS != ret) break; 158 boolean_t scan = (info.protection & VM_PROT_WRITE) ? 1 : 0; 159 if (scan) { 160 uintptr_t *addr = (uintptr_t *)((uintptr_t)address); 161 uintptr_t *end = (uintptr_t *)((uintptr_t)address + (uintptr_t)size); 162 while (addr < end) { 163 if ((uintptr_t *)start <= addr && *addr == ptr) { 164 return (uintptr_t)addr; 165 } 166 addr++; 167 } 168 } 169 address += size; 170 } 171 return 0; 172} 173 174CF_PRIVATE void __CFDumpAllPointerLocations(uintptr_t ptr) { 175 uintptr_t addr = 0; 176 do { 177 addr = __CFFindPointer(ptr, sizeof(void *) + addr); 178 printf("%p\n", (void *)addr); 179 } while (addr != 0); 180} 181#endif 182 183#if DEPLOYMENT_TARGET_WINDOWS 184struct _args { 185 void *func; 186 void *arg; 187 HANDLE handle; 188}; 189static unsigned __stdcall __CFWinThreadFunc(void *arg) { 190 struct _args *args = (struct _args*)arg; 191 ((void (*)(void *))args->func)(args->arg); 192 CloseHandle(args->handle); 193 CFAllocatorDeallocate(kCFAllocatorSystemDefault, arg); 194 _endthreadex(0); 195 return 0; 196} 197#endif 198 199CF_PRIVATE void *__CFStartSimpleThread(void *func, void *arg) { 200#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD 201 pthread_attr_t attr; 202 pthread_t tid = 0; 203 pthread_attr_init(&attr); 204 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); 205 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 206 pthread_attr_setstacksize(&attr, 60 * 1024); // 60K stack for our internal threads is sufficient 207 OSMemoryBarrier(); // ensure arg is fully initialized and set in memory 208 pthread_create(&tid, &attr, func, arg); 209 pthread_attr_destroy(&attr); 210//warning CF: we dont actually know that a pthread_t is the same size as void * 211 return (void *)tid; 212#elif DEPLOYMENT_TARGET_WINDOWS 213 unsigned tid; 214 struct _args *args = (struct _args*)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(struct _args), 0); 215 if (__CFOASafe) __CFSetLastAllocationEventName(args, "CFUtilities (thread-args)"); 216 HANDLE handle; 217 args->func = func; 218 args->arg = arg; 219 /* The thread is created suspended, because otherwise there would be a race between the assignment below of the handle field, and it's possible use in the thread func above. */ 220 args->handle = (HANDLE)_beginthreadex(NULL, 0, __CFWinThreadFunc, args, CREATE_SUSPENDED, &tid); 221 handle = args->handle; 222 ResumeThread(handle); 223 return handle; 224#endif 225} 226 227 228// Looks for localized version of "nonLocalized" in the SystemVersion bundle 229// If not found, and returnNonLocalizedFlag == true, will return the non localized string (retained of course), otherwise NULL 230// If bundlePtr != NULL, will use *bundlePtr and will return the bundle in there; otherwise bundle is created and released 231#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 232static CFStringRef _CFCopyLocalizedVersionKey(CFBundleRef *bundlePtr, CFStringRef nonLocalized) { 233 CFStringRef localized = NULL; 234 CFBundleRef locBundle = bundlePtr ? *bundlePtr : NULL; 235 if (!locBundle) { 236 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, CFSTR("/System/Library/CoreServices/SystemVersion.bundle"), kCFURLPOSIXPathStyle, false); 237 if (url) { 238 locBundle = CFBundleCreate(kCFAllocatorSystemDefault, url); 239 CFRelease(url); 240 } 241 } 242 if (locBundle) { 243 localized = CFBundleCopyLocalizedString(locBundle, nonLocalized, nonLocalized, CFSTR("SystemVersion")); 244 if (bundlePtr) *bundlePtr = locBundle; else CFRelease(locBundle); 245 } 246 return localized ? localized : (CFStringRef)CFRetain(nonLocalized); 247} 248#endif 249 250static CFDictionaryRef _CFCopyVersionDictionary(CFStringRef path) { 251 CFPropertyListRef plist = NULL; 252 253#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 254 CFDataRef data; 255 CFURLRef url; 256 257 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, path, kCFURLPOSIXPathStyle, false); 258#pragma GCC diagnostic push 259#pragma GCC diagnostic ignored "-Wdeprecated" 260 if (url && CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, url, &data, NULL, NULL, NULL)) { 261#pragma GCC diagnostic pop 262 plist = CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListMutableContainers, NULL); 263 CFRelease(data); 264 } 265 if (url) CFRelease(url); 266 267 if (plist) { 268#if DEPLOYMENT_TARGET_EMBEDDED_MINI 269 CFStringRef fullVersion, vers, versExtra, build; 270 CFStringRef versionString = CFRetain(_kCFSystemVersionProductVersionStringKey); 271 CFStringRef buildString = CFRetain(_kCFSystemVersionBuildStringKey); 272 CFStringRef fullVersionString = CFRetain(CFSTR("FullVersionString")); 273#else 274 CFBundleRef locBundle = NULL; 275 CFStringRef fullVersion, vers, versExtra, build; 276 CFStringRef versionString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionProductVersionStringKey); 277 CFStringRef buildString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionBuildStringKey); 278 CFStringRef fullVersionString = _CFCopyLocalizedVersionKey(&locBundle, CFSTR("FullVersionString")); 279 if (locBundle) CFRelease(locBundle); 280#endif 281 282 // Now build the full version string 283 if (CFEqual(fullVersionString, CFSTR("FullVersionString"))) { 284 CFRelease(fullVersionString); 285 fullVersionString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@ %%@ (%@ %%@)"), versionString, buildString); 286 } 287 vers = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionProductVersionKey); 288 versExtra = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionProductVersionExtraKey); 289 if (vers && versExtra) vers = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@ %@"), vers, versExtra); 290 build = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionBuildVersionKey); 291 fullVersion = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, fullVersionString, (vers ? vers : CFSTR("?")), build ? build : CFSTR("?")); 292 if (vers && versExtra) CFRelease(vers); 293 294 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductVersionStringKey, versionString); 295 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionBuildStringKey, buildString); 296 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR("FullVersionString"), fullVersion); 297 CFRelease(versionString); 298 CFRelease(buildString); 299 CFRelease(fullVersionString); 300 CFRelease(fullVersion); 301 } 302#elif DEPLOYMENT_TARGET_WINDOWS 303 OSVERSIONINFOEX osvi; 304 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); 305 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); 306 BOOL result = GetVersionEx((OSVERSIONINFO *)&osvi); 307 if (!result) return NULL; 308 309 plist = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 10, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 310 311 // e.g. 10.7 312 CFStringRef versionString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%ld.%ld(%ld,%ld)"), osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor); 313 314 // e.g. 11A508 315 CFStringRef buildString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%ld"), osvi.dwBuildNumber); 316 317 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductVersionKey, versionString); 318 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionBuildVersionKey, buildString); 319 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductNameKey, CFSTR("Windows")); // hard coded for now 320 321 CFRelease(versionString); 322 CFRelease(buildString); 323#endif 324 return (CFDictionaryRef)plist; 325} 326 327CFStringRef CFCopySystemVersionString(void) { 328 CFStringRef versionString; 329 CFDictionaryRef dict = _CFCopyServerVersionDictionary(); 330 if (!dict) dict = _CFCopySystemVersionDictionary(); 331 if (!dict) return NULL; 332 versionString = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("FullVersionString")); 333 if (versionString) CFRetain(versionString); 334 CFRelease(dict); 335 return versionString; 336} 337 338// Obsolete: These two functions cache the dictionaries to avoid calling _CFCopyVersionDictionary() more than once per dict desired 339// In fact, they do not cache any more, because the file can change after 340// apps are running in some situations, and apps need the new info. 341// Proper caching and testing to see if the file has changed, without race 342// conditions, would require semi-convoluted use of fstat(). 343 344CFDictionaryRef _CFCopySystemVersionDictionary(void) { 345 CFPropertyListRef plist = NULL; 346 plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/SystemVersion.plist")); 347 return (CFDictionaryRef)plist; 348} 349 350CFDictionaryRef _CFCopyServerVersionDictionary(void) { 351 CFPropertyListRef plist = NULL; 352 plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/ServerVersion.plist")); 353 return (CFDictionaryRef)plist; 354} 355 356CONST_STRING_DECL(_kCFSystemVersionProductNameKey, "ProductName") 357CONST_STRING_DECL(_kCFSystemVersionProductCopyrightKey, "ProductCopyright") 358CONST_STRING_DECL(_kCFSystemVersionProductVersionKey, "ProductVersion") 359CONST_STRING_DECL(_kCFSystemVersionProductVersionExtraKey, "ProductVersionExtra") 360CONST_STRING_DECL(_kCFSystemVersionProductUserVisibleVersionKey, "ProductUserVisibleVersion") 361CONST_STRING_DECL(_kCFSystemVersionBuildVersionKey, "ProductBuildVersion") 362CONST_STRING_DECL(_kCFSystemVersionProductVersionStringKey, "Version") 363CONST_STRING_DECL(_kCFSystemVersionBuildStringKey, "Build") 364 365 366CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version) { 367 return true; 368} 369 370 371 372 373#if DEPLOYMENT_TARGET_MACOSX 374CF_PRIVATE void *__CFLookupCarbonCoreFunction(const char *name) { 375 static void *image = NULL; 376 if (NULL == image) { 377 image = dlopen("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", RTLD_LAZY | RTLD_LOCAL); 378 } 379 void *dyfunc = NULL; 380 if (image) { 381 dyfunc = dlsym(image, name); 382 } 383 return dyfunc; 384} 385#endif 386 387#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 388CF_PRIVATE void *__CFLookupCoreServicesInternalFunction(const char *name) { 389 static void *image = NULL; 390 if (NULL == image) { 391 image = dlopen("/System/Library/PrivateFrameworks/CoreServicesInternal.framework/CoreServicesInternal", RTLD_LAZY | RTLD_LOCAL); 392 } 393 void *dyfunc = NULL; 394 if (image) { 395 dyfunc = dlsym(image, name); 396 } 397 return dyfunc; 398} 399 400CF_PRIVATE void *__CFLookupCFNetworkFunction(const char *name) { 401 static void *image = NULL; 402 if (NULL == image) { 403 const char *path = NULL; 404 if (!__CFProcessIsRestricted()) { 405 path = __CFgetenv("CFNETWORK_LIBRARY_PATH"); 406 } 407 if (!path) { 408 path = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork"; 409 } 410 image = dlopen(path, RTLD_LAZY | RTLD_LOCAL); 411 } 412 void *dyfunc = NULL; 413 if (image) { 414 dyfunc = dlsym(image, name); 415 } 416 return dyfunc; 417} 418#endif 419 420 421#ifndef __CFGetSessionID_defined 422 423CF_PRIVATE uint32_t __CFGetSessionID(void) { 424 return 0; 425} 426 427#endif 428 429CF_PRIVATE CFIndex __CFActiveProcessorCount() { 430 int32_t pcnt; 431#if DEPLOYMENT_TARGET_WINDOWS 432 SYSTEM_INFO sysInfo; 433 GetSystemInfo(&sysInfo); 434 DWORD_PTR activeProcessorMask = sysInfo.dwActiveProcessorMask; 435 // assumes sizeof(DWORD_PTR) is 64 bits or less 436 uint64_t v = activeProcessorMask; 437 v = v - ((v >> 1) & 0x5555555555555555ULL); 438 v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL); 439 v = (v + (v >> 4)) & 0xf0f0f0f0f0f0f0fULL; 440 pcnt = (v * 0x0101010101010101ULL) >> ((sizeof(v) - 1) * 8); 441#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 442 int32_t mib[] = {CTL_HW, HW_AVAILCPU}; 443 size_t len = sizeof(pcnt); 444 int32_t result = sysctl(mib, sizeof(mib) / sizeof(int32_t), &pcnt, &len, NULL, 0); 445 if (result != 0) { 446 pcnt = 0; 447 } 448#else 449 // Assume the worst 450 pcnt = 1; 451#endif 452 return pcnt; 453} 454 455CF_PRIVATE void __CFGetUGIDs(uid_t *euid, gid_t *egid) { 456#if 1 && (DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI) 457 uid_t uid; 458 gid_t gid; 459 if (0 == pthread_getugid_np(&uid, &gid)) { 460 if (euid) *euid = uid; 461 if (egid) *egid = gid; 462 } else 463#endif 464 { 465 if (euid) *euid = geteuid(); 466 if (egid) *egid = getegid(); 467 } 468} 469 470const char *_CFPrintForDebugger(const void *obj) { 471 static char *result = NULL; 472 CFStringRef str; 473 CFIndex cnt = 0; 474 475 free(result); // Let go of result from previous call. 476 result = NULL; 477 if (obj) { 478 if (CFGetTypeID(obj) == CFStringGetTypeID()) { 479 // Makes Ali marginally happier 480 str = __CFCopyFormattingDescription(obj, NULL); 481 if (!str) str = CFCopyDescription(obj); 482 } else { 483 str = CFCopyDescription(obj); 484 } 485 } else { 486 str = (CFStringRef)CFRetain(CFSTR("(null)")); 487 } 488 489 if (str != NULL) { 490 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, FALSE, NULL, 0, &cnt); 491 } 492 result = (char *) malloc(cnt + 2); // 1 for '\0', 1 for an optional '\n' 493 if (str != NULL) { 494 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, FALSE, (UInt8 *) result, cnt, &cnt); 495 } 496 result[cnt] = '\0'; 497 498 if (str) CFRelease(str); 499 return result; 500} 501 502static void _CFShowToFile(FILE *file, Boolean flush, const void *obj) { 503 CFStringRef str; 504 CFIndex idx, cnt; 505 CFStringInlineBuffer buffer; 506 bool lastNL = false; 507 508 if (obj) { 509 if (CFGetTypeID(obj) == CFStringGetTypeID()) { 510 // Makes Ali marginally happier 511 str = __CFCopyFormattingDescription(obj, NULL); 512 if (!str) str = CFCopyDescription(obj); 513 } else { 514 str = CFCopyDescription(obj); 515 } 516 } else { 517 str = (CFStringRef)CFRetain(CFSTR("(null)")); 518 } 519 cnt = CFStringGetLength(str); 520 521#if DEPLOYMENT_TARGET_WINDOWS 522 UniChar *ptr = (UniChar *)CFStringGetCharactersPtr(str); 523 BOOL freePtr = false; 524 if (!ptr) { 525 CFIndex strLen = CFStringGetLength(str); 526 // +2, 1 for newline, 1 for null 527 CFIndex bufSize = sizeof(UniChar *) * (CFStringGetMaximumSizeForEncoding(strLen, kCFStringEncodingUnicode) + 2); 528 CFIndex bytesUsed = 0; 529 ptr = (UniChar *)malloc(bufSize); 530 CFStringGetCharacters(str, CFRangeMake(0, strLen), ptr); 531 ptr[strLen] = L'\n'; 532 ptr[strLen+1] = 0; 533 freePtr = true; 534 } 535 OutputDebugStringW((wchar_t *)ptr); 536 if (freePtr) free(ptr); 537#else 538 CFStringInitInlineBuffer(str, &buffer, CFRangeMake(0, cnt)); 539 for (idx = 0; idx < cnt; idx++) { 540 UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&buffer, idx); 541 if (ch < 128) { 542 fprintf_l(file, NULL, "%c", ch); 543 lastNL = (ch == '\n'); 544 } else { 545 fprintf_l(file, NULL, "\\u%04x", ch); 546 } 547 } 548 if (!lastNL) { 549#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 550 fprintf_l(file, NULL, "\n"); 551#else 552 fprintf(file, NULL, "\n"); 553#endif 554 if (flush) fflush(file); 555 } 556#endif 557 558 if (str) CFRelease(str); 559} 560 561void CFShow(const void *obj) { 562 _CFShowToFile(stderr, true, obj); 563} 564 565 566// message must be a UTF8-encoded, null-terminated, byte buffer with at least length bytes 567typedef void (*CFLogFunc)(int32_t lev, const char *message, size_t length, char withBanner); 568 569static Boolean also_do_stderr() { 570#if DEPLOYMENT_TARGET_EMBEDDED_MINI 571 // just log to stderr, other logging facilities are out 572 return true; 573#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 574 if (!issetugid() && __CFgetenv("CFLOG_FORCE_STDERR")) { 575 return true; 576 } 577 struct stat sb; 578 int ret = fstat(STDERR_FILENO, &sb); 579 if (ret < 0) return false; 580 mode_t m = sb.st_mode & S_IFMT; 581 if (S_IFREG == m || S_IFSOCK == m) return true; 582 if (!(S_IFIFO == m || S_IFCHR == m)) return false; // disallow any whacky stuff 583#if 0 // launchd no longer repeats everything it hears 584 // if it could be a pipe back to launchd, fail 585 int64_t val = 0; 586 // assumes val is not written to on error 587 vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &val); 588 if (val) return false; 589#endif 590#endif 591 return true; 592} 593 594extern char *__CFBundleMainID; 595 596static void __CFLogCString(int32_t lev, const char *message, size_t length, char withBanner) { 597 char *banner = NULL; 598 char *time = NULL; 599 char *thread = NULL; 600 char *uid = NULL; 601 int bannerLen; 602 bannerLen = 0; 603#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX 604 // The banner path may use CF functions, but the rest of this function should not. It may be called at times when CF is not fully setup or torn down. 605 if (withBanner) { 606 CFAbsoluteTime at = CFAbsoluteTimeGetCurrent(); 607 CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFCalendarIdentifierGregorian); 608 if (!calendar) goto after_banner; 609 CFTimeZoneRef tz = CFTimeZoneCopySystem(); 610 if (!tz) { 611 CFRelease(calendar); 612 goto after_banner; 613 } 614 CFCalendarSetTimeZone(calendar, tz); 615 CFRelease(tz); 616 int32_t year, month, day, hour, minute, second; 617 Boolean dec = CFCalendarDecomposeAbsoluteTime(calendar, at, "yMdHms", &year, &month, &day, &hour, &minute, &second); 618 CFRelease(calendar); 619 if (!dec) goto after_banner; 620 double atf; 621 int32_t ms = (int32_t)floor(1000.0 * modf(at, &atf)); 622#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 623 asprintf(&banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%x] ", year, month, day, hour, minute, second, ms, *_CFGetProgname(), getpid(), pthread_mach_thread_np(pthread_self())); 624 asprintf(&thread, "%x", pthread_mach_thread_np(pthread_self())); 625#elif DEPLOYMENT_TARGET_WINDOWS 626 bannerLen = asprintf(&banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%x] ", year, month, day, hour, minute, second, ms, *_CFGetProgname(), getpid(), GetCurrentThreadId()); 627 asprintf(&thread, "%x", GetCurrentThreadId()); 628#else 629 bannerLen = asprintf(&banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%x] ", year, month, day, hour, minute, second, ms, *_CFGetProgname(), getpid(), (unsigned int)pthread_self()); 630 asprintf(&thread, "%lx", pthread_self()); 631#endif 632 asprintf(&time, "%04d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, ms); 633 634 } 635 after_banner:; 636#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS 637 uid_t euid; 638 __CFGetUGIDs(&euid, NULL); 639 asprintf(&uid, "%d", euid); 640 aslclient asl = asl_open(NULL, __CFBundleMainID[0] ? __CFBundleMainID : "com.apple.console", ASL_OPT_NO_DELAY); 641 aslmsg msg = asl_new(ASL_TYPE_MSG); 642 asl_set(msg, "CFLog Local Time", time); // not to be documented, not public API 643 asl_set(msg, "CFLog Thread", thread); // not to be documented, not public API 644 asl_set(msg, "ReadUID", uid); 645 static const char *levstr[] = {"0", "1", "2", "3", "4", "5", "6", "7"}; 646 asl_set(msg, ASL_KEY_LEVEL, levstr[lev]); 647 asl_set(msg, ASL_KEY_MSG, message); 648 asl_send(asl, msg); 649 asl_free(msg); 650 asl_close(asl); 651#endif 652#endif // DEPLOYMENT_TARGET 653 654 if (also_do_stderr()) { 655#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 656 struct iovec v[3]; 657 v[0].iov_base = banner; 658 v[0].iov_len = banner ? strlen(banner) : 0; 659 v[1].iov_base = (char *)message; 660 v[1].iov_len = length; 661 v[2].iov_base = "\n"; 662 v[2].iov_len = (message[length - 1] != '\n') ? 1 : 0; 663 int nv = (v[0].iov_base ? 1 : 0) + 1 + (v[2].iov_len ? 1 : 0); 664 static CFSpinLock_t lock = CFSpinLockInit; 665 __CFSpinLock(&lock); 666 writev(STDERR_FILENO, v[0].iov_base ? v : v + 1, nv); 667 __CFSpinUnlock(&lock); 668#elif DEPLOYMENT_TARGET_WINDOWS 669 size_t bufLen = bannerLen + length + 1; 670 char *buf = (char *)malloc(sizeof(char) * bufLen); 671 if (banner) { 672 // Copy the banner into the debug string 673 memmove_s(buf, bufLen, banner, bannerLen); 674 675 // Copy the message into the debug string 676 strcpy_s(buf + bannerLen, bufLen - bannerLen, message); 677 } else { 678 strcpy_s(buf, bufLen, message); 679 } 680 buf[bufLen - 1] = '\0'; 681 fprintf_s(stderr, "%s\n", buf); 682 // This Win32 API call only prints when a debugger is active 683 // OutputDebugStringA(buf); 684 free(buf); 685#else 686 size_t bufLen = bannerLen + length + 1; 687 char *buf = (char *)malloc(sizeof(char) * bufLen); 688 if (banner) { 689 // Copy the banner into the debug string 690 memmove(buf, banner, bannerLen); 691 692 // Copy the message into the debug string 693 strncpy(buf + bannerLen, message, bufLen - bannerLen); 694 } else { 695 strncpy(buf, message, bufLen); 696 } 697 buf[bufLen - 1] = '\0'; 698 fprintf(stderr, "%s\n", buf); 699 free(buf); 700#endif 701 } 702 703 if (thread) free(thread); 704 if (time) free(time); 705 if (banner) free(banner); 706 if (uid) free(uid); 707} 708 709CF_EXPORT void _CFLogvEx(CFLogFunc logit, CFStringRef (*copyDescFunc)(void *, const void *), CFDictionaryRef formatOptions, int32_t lev, CFStringRef format, va_list args) { 710#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 711 uintptr_t val = (uintptr_t)_CFGetTSD(__CFTSDKeyIsInCFLog); 712 if (3 < val) return; // allow up to 4 nested invocations 713 _CFSetTSD(__CFTSDKeyIsInCFLog, (void *)(val + 1), NULL); 714#endif 715 CFStringRef str = format ? _CFStringCreateWithFormatAndArgumentsAux(kCFAllocatorSystemDefault, copyDescFunc, formatOptions, (CFStringRef)format, args) : 0; 716 CFIndex blen = str ? CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1 : 0; 717 char *buf = str ? (char *)malloc(blen) : 0; 718 if (str && buf) { 719 Boolean converted = CFStringGetCString(str, buf, blen, kCFStringEncodingUTF8); 720 size_t len = strlen(buf); 721 // silently ignore 0-length or really large messages, and levels outside the valid range 722 if (converted && !(len <= 0 || (1 << 24) < len) && !(lev < ASL_LEVEL_EMERG || ASL_LEVEL_DEBUG < lev)) { 723 (logit ? logit : __CFLogCString)(lev, buf, len, 1); 724 } 725 } 726 if (buf) free(buf); 727 if (str) CFRelease(str); 728#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 729 _CFSetTSD(__CFTSDKeyIsInCFLog, (void *)val, NULL); 730#endif 731} 732 733// This CF-only log function uses no CF functionality, so it may be called anywhere within CF - including thread teardown or prior to full CF setup 734CF_PRIVATE void _CFLogSimple(int32_t lev, char *format, ...) { 735 va_list args; 736 va_start(args, format); 737 char formattedMessage[1024]; 738 int length = vsnprintf(formattedMessage, 1024, format, args); 739 if (length > 0) { 740 __CFLogCString(lev, formattedMessage, length, 0); 741 } 742 va_end(args); 743} 744 745void CFLog(int32_t lev, CFStringRef format, ...) { 746 va_list args; 747 va_start(args, format); 748 _CFLogvEx(NULL, NULL, NULL, lev, format, args); 749 va_end(args); 750} 751 752 753 754#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 755 756kern_return_t _CFDiscorporateMemoryAllocate(CFDiscorporateMemory *hm, size_t size, bool purgeable) { 757 kern_return_t ret = KERN_SUCCESS; 758 size = round_page(size); 759 if (0 == size) size = vm_page_size; 760 memset(hm, 0, sizeof(CFDiscorporateMemory)); 761 void *addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, VM_MAKE_TAG(0) | (purgeable ? VM_FLAGS_PURGABLE : 0), 0); 762 if ((uintptr_t)addr == -1) { 763 ret = KERN_NO_SPACE; 764 } 765 if (KERN_SUCCESS == ret) { 766 hm->address = (mach_vm_address_t)(uintptr_t)addr; 767 hm->size = (mach_vm_size_t)size; 768 hm->port = MACH_PORT_NULL; 769 hm->corporeal = true; 770 hm->purgeable = purgeable; 771 } 772 if (KERN_SUCCESS == ret) ret = mach_make_memory_entry_64(mach_task_self(), &hm->size, hm->address, VM_PROT_DEFAULT, &hm->port, MACH_PORT_NULL); 773 if (KERN_SUCCESS == ret) hm->corporeal = true; 774 return ret; 775} 776 777kern_return_t _CFDiscorporateMemoryDeallocate(CFDiscorporateMemory *hm) { 778 kern_return_t ret1 = KERN_SUCCESS, ret2 = KERN_SUCCESS; 779 if (hm->corporeal) ret1 = mach_vm_deallocate(mach_task_self(), hm->address, hm->size); 780 hm->address = MACH_VM_MIN_ADDRESS; 781 hm->corporeal = false; 782 ret2 = mach_port_deallocate(mach_task_self(), hm->port); 783 hm->port = MACH_PORT_NULL; 784 return ret1 != KERN_SUCCESS ? ret1 : ret2; 785} 786 787kern_return_t _CFDiscorporateMemoryDematerialize(CFDiscorporateMemory *hm) { 788 kern_return_t ret = KERN_SUCCESS; 789 if (!hm->corporeal) ret = KERN_INVALID_MEMORY_CONTROL; 790 int state = VM_PURGABLE_VOLATILE; 791 if (KERN_SUCCESS == ret) vm_purgable_control(mach_task_self(), (vm_address_t)hm->address, VM_PURGABLE_SET_STATE, &state); 792 if (KERN_SUCCESS == ret) ret = mach_vm_deallocate(mach_task_self(), hm->address, hm->size); 793 if (KERN_SUCCESS == ret) hm->address = MACH_VM_MIN_ADDRESS; 794 if (KERN_SUCCESS == ret) hm->corporeal = false; 795 return ret; 796} 797 798kern_return_t _CFDiscorporateMemoryMaterialize(CFDiscorporateMemory *hm) { 799 kern_return_t ret = KERN_SUCCESS; 800 if (hm->corporeal) ret = KERN_INVALID_MEMORY_CONTROL; 801 if (KERN_SUCCESS == ret) ret = mach_vm_map(mach_task_self(), &hm->address, hm->size, 0, VM_FLAGS_ANYWHERE, hm->port, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_DEFAULT); 802 if (KERN_SUCCESS == ret) hm->corporeal = true; 803 int state = VM_PURGABLE_NONVOLATILE; 804 if (KERN_SUCCESS == ret) ret = vm_purgable_control(mach_task_self(), (vm_address_t)hm->address, VM_PURGABLE_SET_STATE, &state); 805 if (KERN_SUCCESS == ret) if (VM_PURGABLE_EMPTY == state) ret = KERN_PROTECTION_FAILURE; // same as VM_PURGABLE_EMPTY 806 return ret; 807} 808 809#endif 810 811#if DEPLOYMENT_TARGET_MACOSX 812 813#define SUDDEN_TERMINATION_ENABLE_VPROC 1 814 815#if SUDDEN_TERMINATION_ENABLE_VPROC 816 817static CFSpinLock_t __CFProcessKillingLock = CFSpinLockInit; 818static CFIndex __CFProcessKillingDisablingCount = 1; 819static Boolean __CFProcessKillingWasTurnedOn = false; 820 821void _CFSuddenTerminationDisable(void) { 822 __CFSpinLock(&__CFProcessKillingLock); 823 __CFProcessKillingDisablingCount++; 824 _vproc_transaction_begin(); 825 __CFSpinUnlock(&__CFProcessKillingLock); 826} 827 828void _CFSuddenTerminationEnable(void) { 829 // In our model the first call of _CFSuddenTerminationEnable() that does not balance a previous call of _CFSuddenTerminationDisable() actually enables sudden termination so we have to keep a count that's almost redundant with vproc's. 830 __CFSpinLock(&__CFProcessKillingLock); 831 __CFProcessKillingDisablingCount--; 832 if (__CFProcessKillingDisablingCount==0 && !__CFProcessKillingWasTurnedOn) { 833 _vproc_transactions_enable(); 834 __CFProcessKillingWasTurnedOn = true; 835 } else { 836 // Mail seems to have sudden termination disabling/enabling imbalance bugs that make _vproc_transaction_end() kill the app but we don't want that to prevent our submission of the fix 6382488. 837 if (__CFProcessKillingDisablingCount>=0) { 838 _vproc_transaction_end(); 839 } else { 840 CFLog(kCFLogLevelError, CFSTR("-[NSProcessInfo enableSuddenTermination] has been invoked more times than necessary to balance invocations of -[NSProcessInfo disableSuddenTermination]. Ignoring.")); 841 } 842 } 843 __CFSpinUnlock(&__CFProcessKillingLock); 844} 845 846void _CFSuddenTerminationExitIfTerminationEnabled(int exitStatus) { 847 // This is for when the caller wants to try to exit quickly if possible but not automatically exit the process when it next becomes clean, because quitting might still be cancelled by the user. 848 __CFSpinLock(&__CFProcessKillingLock); 849 // Check _vproc_transaction_count() because other code in the process might go straight to the vproc APIs but also check __CFProcessKillingWasTurnedOn because _vproc_transaction_count() can return 0 when transactions didn't even get enabled. 850 if (_vproc_transaction_count()==0 && __CFProcessKillingWasTurnedOn) { 851 _exit(exitStatus); 852 } 853 __CFSpinUnlock(&__CFProcessKillingLock); 854} 855 856void _CFSuddenTerminationExitWhenTerminationEnabled(int exitStatus) { 857 // The user has had their final opportunity to cancel quitting. Exit as soon as the process is clean. Same carefulness as in _CFSuddenTerminationExitIfTerminationEnabled(). 858 __CFSpinLock(&__CFProcessKillingLock); 859 if (__CFProcessKillingWasTurnedOn) { 860 _vproc_transaction_try_exit(exitStatus); 861 } 862 __CFSpinUnlock(&__CFProcessKillingLock); 863} 864 865size_t _CFSuddenTerminationDisablingCount(void) { 866 // Until sudden termination has been really enabled vproc's notion of the count is off by one but we can't just return __CFProcessKillingDisablingCount() because that doesn't take into account stuff that calls the vproc_transaction functions behind our back. 867 return _vproc_transaction_count() + (__CFProcessKillingWasTurnedOn ? 0 : 1); 868} 869 870#else 871 872#warning Building with vproc sudden termination API disabled. 873 874static CFSpinLock_t __CFProcessKillingLock = CFSpinLockInit; 875static size_t __CFProcessKillingDisablingCount = 1; 876static Boolean __CFProcessExitNextTimeKillingIsEnabled = false; 877static int32_t __CFProcessExitStatus = 0; 878static int __CFProcessIsKillableNotifyToken; 879static Boolean __CFProcessIsKillableNotifyTokenIsFigured = false; 880 881CF_PRIVATE void _CFSetSuddenTerminationEnabled(Boolean isEnabled) { 882 if (!__CFProcessIsKillableNotifyTokenIsFigured) { 883 char *notificationName = NULL; 884 asprintf(¬ificationName, "com.apple.isKillable.%i", getpid()); 885 uint32_t notifyResult = notify_register_check(notificationName, &__CFProcessIsKillableNotifyToken); 886 if (notifyResult != NOTIFY_STATUS_OK) { 887 CFLog(kCFLogLevelError, CFSTR("%s: notify_register_check() returned %i."), __PRETTY_FUNCTION__, notifyResult); 888 } 889 free(notificationName); 890 __CFProcessIsKillableNotifyTokenIsFigured = true; 891 } 892 uint32_t notifyResult = notify_set_state(__CFProcessIsKillableNotifyToken, isEnabled); 893 if (notifyResult != NOTIFY_STATUS_OK) { 894 CFLog(kCFLogLevelError, CFSTR("%s: notify_set_state() returned %i"), __PRETTY_FUNCTION__, notifyResult); 895 } 896} 897 898void _CFSuddenTerminationDisable(void) { 899 __CFSpinLock(&__CFProcessKillingLock); 900 if (__CFProcessKillingDisablingCount == 0) { 901 _CFSetSuddenTerminationEnabled(false); 902 } 903 __CFProcessKillingDisablingCount++; 904 __CFSpinUnlock(&__CFProcessKillingLock); 905} 906 907void _CFSuddenTerminationEnable(void) { 908 __CFSpinLock(&__CFProcessKillingLock); 909 __CFProcessKillingDisablingCount--; 910 if (__CFProcessKillingDisablingCount == 0) { 911 if (__CFProcessExitNextTimeKillingIsEnabled) { 912 _exit(__CFProcessExitStatus); 913 } else { 914 _CFSetSuddenTerminationEnabled(true); 915 } 916 } 917 __CFSpinUnlock(&__CFProcessKillingLock); 918} 919 920void _CFSuddenTerminationExitIfTerminationEnabled(int exitStatus) { 921 __CFSpinLock(&__CFProcessKillingLock); 922 if (__CFProcessKillingDisablingCount == 0) { 923 _exit(exitStatus); 924 } 925 __CFSpinUnlock(&__CFProcessKillingLock); 926} 927 928void _CFSuddenTerminationExitWhenTerminationEnabled(int exitStatus) { 929 __CFSpinLock(&__CFProcessKillingLock); 930 if (__CFProcessKillingDisablingCount == 0) { 931 _exit(exitStatus); 932 } else { 933 __CFProcessExitNextTimeKillingIsEnabled = YES; 934 __CFProcessExitStatus = exitStatus; 935 } 936 __CFSpinUnlock(&__CFProcessKillingLock); 937} 938 939size_t _CFSuddenTerminationDisablingCount(void) { 940 return __CFProcessKillingDisablingCount; 941} 942 943#endif 944 945#endif 946 947#if 0 948#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 949 950typedef void (^ThrottleTypeA)(void); // allows calls per nanoseconds 951typedef void (^ThrottleTypeB)(uint64_t amt); // allows amount per nanoseconds 952 953CF_PRIVATE ThrottleTypeA __CFCreateThrottleTypeA(uint16_t calls, uint64_t nanoseconds) { 954 struct mach_timebase_info info; 955 mach_timebase_info(&info); 956 uint64_t period = nanoseconds / info.numer * info.denom; 957 958 if (0 == calls || 0 == period) return NULL; 959 960 __block OSSpinLock b_lock = OS_SPINLOCK_INIT; 961 __block uint64_t b_values[calls]; 962 __block uint64_t *b_oldest = b_values; 963 memset(b_values, 0, sizeof(b_values)); 964 965 return Block_copy(^{ 966 uint64_t curr_time = mach_absolute_time(); 967 OSSpinLockLock(&b_lock); 968 uint64_t next_time = *b_oldest + period; 969 *b_oldest = (curr_time < next_time) ? next_time : curr_time; 970 b_oldest++; 971 if (b_values + calls <= b_oldest) b_oldest = b_values; 972 OSSpinLockUnlock(&b_lock); 973 if (curr_time < next_time) { 974 mach_wait_until(next_time); 975 } 976 }); 977} 978 979CF_PRIVATE ThrottleTypeB __CFCreateThrottleTypeB(uint64_t amount, uint64_t nanoseconds) { 980 struct mach_timebase_info info; 981 mach_timebase_info(&info); 982 uint64_t period = nanoseconds / info.numer * info.denom; 983 984 if (0 == amount || 0 == period) return NULL; 985 986 __block OSSpinLock b_lock = OS_SPINLOCK_INIT; 987 __block uint64_t b_sum = 0ULL; 988 __block uint16_t b_num_values = 8; 989 __block uint64_t *b_values = calloc(b_num_values, 2 * sizeof(uint64_t)); 990 __block uint64_t *b_oldest = b_values; 991 992 return Block_copy(^(uint64_t amt){ 993 OSSpinLockLock(&b_lock); 994// unimplemented 995 OSSpinLockUnlock(&b_lock); 996 }); 997} 998 999#endif 1000#endif 1001 1002#pragma mark File Reading 1003 1004#include <sys/stat.h> 1005#include <fcntl.h> 1006#include <errno.h> 1007#if DEPLOYMENT_TARGET_WINDOWS 1008#include <io.h> 1009#include <direct.h> 1010#define close _close 1011#define write _write 1012#define read _read 1013#define open _NS_open 1014#define stat _NS_stat 1015#define fstat _fstat 1016#define statinfo _stat 1017 1018#define mach_task_self() 0 1019 1020#else 1021#define statinfo stat 1022#endif 1023 1024static CFErrorRef _CFErrorWithFilePathCodeDomain(CFStringRef domain, CFIndex code, CFStringRef path) { 1025 CFStringRef key = CFSTR("NSFilePath"); 1026 CFDictionaryRef userInfo = CFDictionaryCreate(kCFAllocatorSystemDefault, (const void **)&key, (const void **)&path, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1027 CFErrorRef result = CFErrorCreate(kCFAllocatorSystemDefault, domain, code, userInfo); 1028 CFRelease(userInfo); 1029 return result; 1030} 1031 1032// Caller is responsible for freeing memory. munmap() if map == true, else malloc(). 1033CF_PRIVATE Boolean _CFReadMappedFromFile(CFStringRef path, Boolean map, Boolean uncached, void **outBytes, CFIndex *outLength, CFErrorRef *errorPtr) { 1034 void *bytes = NULL; 1035 unsigned long length; 1036 char cpath[CFMaxPathSize]; 1037 if (!CFStringGetFileSystemRepresentation(path, cpath, CFMaxPathSize)) { 1038 // TODO: real error codes 1039 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainCocoa, -1, path); 1040 return false; 1041 } 1042 1043 struct statinfo statBuf; 1044 int32_t fd = -1; 1045 1046 fd = open(cpath, O_RDONLY|CF_OPENFLGS, 0666); 1047 if (fd < 0) { 1048 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, errno, path); 1049 return false; 1050 } 1051#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 1052 if (uncached) (void)fcntl(fd, F_NOCACHE, 1); // Non-zero arg turns off caching; we ignore error as uncached is just a hint 1053#endif 1054 if (fstat(fd, &statBuf) < 0) { 1055 int32_t savederrno = errno; 1056 close(fd); 1057 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path); 1058 return false; 1059 } 1060 if ((statBuf.st_mode & S_IFMT) != S_IFREG) { 1061 close(fd); 1062 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, EACCES, path); 1063 return false; 1064 } 1065 if (statBuf.st_size < 0LL) { // too small 1066 close(fd); 1067 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, ENOMEM, path); 1068 return false; 1069 } 1070#if __LP64__ 1071#else 1072 if (statBuf.st_size > (1LL << 31)) { // refuse to do more than 2GB 1073 close(fd); 1074 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, EFBIG, path); 1075 return false; 1076 } 1077#endif 1078 1079 if (0LL == statBuf.st_size) { 1080 bytes = malloc(8); // don't return constant string -- it's freed! 1081 length = 0; 1082#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX 1083 } else if (map) { 1084 if((void *)-1 == (bytes = mmap(0, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0))) { 1085 int32_t savederrno = errno; 1086 close(fd); 1087 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path); 1088 return false; 1089 } 1090 length = (unsigned long)statBuf.st_size; 1091 } else { 1092 bytes = malloc(statBuf.st_size); 1093 if (bytes == NULL) { 1094 close(fd); 1095 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, ENOMEM, path); 1096 return false; 1097 } 1098 size_t numBytesRemaining = (size_t)statBuf.st_size; 1099 void *readLocation = bytes; 1100 while (numBytesRemaining > 0) { 1101 size_t numBytesRequested = (numBytesRemaining < (1LL << 31)) ? numBytesRemaining : ((1LL << 31) - 1); // This loop is basically a workaround for 4870206 1102 ssize_t numBytesRead = read(fd, readLocation, numBytesRequested); 1103 if (numBytesRead <= 0) { 1104 if (numBytesRead < 0) { 1105 int32_t savederrno = errno; 1106 free(bytes); 1107 close(fd); 1108 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path); 1109 bytes = NULL; 1110 return false; 1111 } else { 1112 // This is a bizarre case; 0 bytes read. Might indicate end-of-file? 1113 break; 1114 } 1115 } else { 1116 readLocation += numBytesRead; 1117 numBytesRemaining -= numBytesRead; 1118 } 1119 } 1120 length = (unsigned long)statBuf.st_size - numBytesRemaining; 1121 } 1122#elif DEPLOYMENT_TARGET_WINDOWS 1123 } else { 1124 bytes = malloc(statBuf.st_size); 1125 DWORD numBytesRead; 1126 if (!ReadFile((HANDLE)_get_osfhandle(fd), bytes, statBuf.st_size, &numBytesRead, NULL)) { 1127 DWORD lastError = GetLastError(); 1128 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, lastError, path); 1129 free(bytes); 1130 close(fd); 1131 errno = lastError; 1132 bytes = NULL; 1133 return false; 1134 } 1135 length = numBytesRead; 1136 } 1137#endif 1138 close(fd); 1139 *outBytes = bytes; 1140 *outLength = length; 1141 return true; 1142} 1143