2 * Copyright (c) 2010 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1998-2009, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
29 #include <CoreFoundation/CFPriv.h>
30 #include "CFInternal.h"
31 #include "CFLocaleInternal.h"
32 #include <CoreFoundation/CFPriv.h>
33 #include <CoreFoundation/CFBundle.h>
34 #include <CoreFoundation/CFURLAccess.h>
35 #include <CoreFoundation/CFPropertyList.h>
36 #include <CoreFoundation/CFTimeZone.h>
37 #include <CoreFoundation/CFCalendar.h>
43 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
46 #include <mach/mach.h>
48 #include <mach-o/loader.h>
49 #include <mach-o/dyld.h>
50 #include <crt_externs.h>
53 #include <vproc_priv.h>
54 #include <sys/sysctl.h>
56 #include <mach/mach.h>
57 #include <mach/mach_vm.h>
60 #include <sys/errno.h>
61 #include <mach/mach_time.h>
62 #include <libkern/OSAtomic.h>
65 #if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
70 /* Comparator is passed the address of the values. */
71 /* Binary searches a sorted-increasing array of some type.
72 Return value is either 1) the index of the element desired,
73 if the target value exists in the list, 2) greater than or
74 equal to count, if the element is greater than all the values
75 in the list, or 3) the index of the element greater than the
78 For example, a search in the list of integers:
88 For instance, if you just care about found/not found:
89 index = CFBSearch(list, count, elem);
90 if (count <= index || list[index] != elem) {
97 __private_extern__ CFIndex
CFBSearch(const void *element
, CFIndex elementSize
, const void *list
, CFIndex count
, CFComparatorFunction comparator
, void *context
) {
98 const char *ptr
= (const char *)list
;
100 CFIndex half
= count
/ 2;
101 const char *probe
= ptr
+ elementSize
* half
;
102 CFComparisonResult cr
= comparator(element
, probe
, context
);
103 if (0 == cr
) return (probe
- (const char *)list
) / elementSize
;
104 ptr
= (cr
< 0) ? ptr
: probe
+ elementSize
;
105 count
= (cr
< 0) ? half
: (half
+ (count
& 1) - 1);
107 return (ptr
- (const char *)list
) / elementSize
;
111 #define ELF_STEP(B) T1 = (H << 4) + B; T2 = T1 & 0xF0000000; if (T2) T1 ^= (T2 >> 24); T1 &= (~T2); H = T1;
113 CFHashCode
CFHashBytes(uint8_t *bytes
, CFIndex length
) {
114 /* The ELF hash algorithm, used in the ELF object file format */
115 UInt32 H
= 0, T1
, T2
;
118 ELF_STEP(bytes
[length
- rem
]);
119 ELF_STEP(bytes
[length
- rem
+ 1]);
120 ELF_STEP(bytes
[length
- rem
+ 2]);
121 ELF_STEP(bytes
[length
- rem
+ 3]);
125 case 3: ELF_STEP(bytes
[length
- 3]);
126 case 2: ELF_STEP(bytes
[length
- 2]);
127 case 1: ELF_STEP(bytes
[length
- 1]);
136 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
137 __private_extern__
uintptr_t __CFFindPointer(uintptr_t ptr
, uintptr_t start
) {
138 vm_map_t task
= mach_task_self();
139 mach_vm_address_t address
= start
;
141 mach_vm_size_t size
= 0;
142 vm_region_basic_info_data_64_t info
;
143 mach_msg_type_number_t count
= VM_REGION_BASIC_INFO_COUNT_64
;
144 mach_port_t object_name
;
145 kern_return_t ret
= mach_vm_region(task
, &address
, &size
, VM_REGION_BASIC_INFO_64
, (vm_region_info_t
)&info
, &count
, &object_name
);
146 if (KERN_SUCCESS
!= ret
) break;
147 boolean_t scan
= (info
.protection
& VM_PROT_WRITE
) ? 1 : 0;
149 uintptr_t *addr
= (uintptr_t *)((uintptr_t)address
);
150 uintptr_t *end
= (uintptr_t *)((uintptr_t)address
+ (uintptr_t)size
);
152 if ((uintptr_t *)start
<= addr
&& *addr
== ptr
) {
153 return (uintptr_t)addr
;
164 #if DEPLOYMENT_TARGET_WINDOWS
170 static unsigned __stdcall
__CFWinThreadFunc(void *arg
) {
171 struct _args
*args
= (struct _args
*)arg
;
172 ((void (*)(void *))args
->func
)(args
->arg
);
173 CloseHandle(args
->handle
);
174 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, arg
);
180 __private_extern__
void *__CFStartSimpleThread(void *func
, void *arg
) {
181 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
184 pthread_attr_init(&attr
);
185 pthread_attr_setscope(&attr
, PTHREAD_SCOPE_SYSTEM
);
186 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
187 pthread_attr_setstacksize(&attr
, 60 * 1024); // 60K stack for our internal threads is sufficient
188 OSMemoryBarrier(); // ensure arg is fully initialized and set in memory
189 pthread_create(&tid
, &attr
, func
, arg
);
190 pthread_attr_destroy(&attr
);
191 //warning CF: we dont actually know that a pthread_t is the same size as void *
193 #elif DEPLOYMENT_TARGET_WINDOWS
195 struct _args
*args
= (struct _args
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(struct _args
), 0);
196 if (__CFOASafe
) __CFSetLastAllocationEventName(args
, "CFUtilities (thread-args)");
200 /* 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. */
201 args
->handle
= (HANDLE
)_beginthreadex(NULL
, 0, __CFWinThreadFunc
, args
, CREATE_SUSPENDED
, &tid
);
202 handle
= args
->handle
;
203 ResumeThread(handle
);
209 // Looks for localized version of "nonLocalized" in the SystemVersion bundle
210 // If not found, and returnNonLocalizedFlag == true, will return the non localized string (retained of course), otherwise NULL
211 // If bundlePtr != NULL, will use *bundlePtr and will return the bundle in there; otherwise bundle is created and released
212 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
213 static CFStringRef
_CFCopyLocalizedVersionKey(CFBundleRef
*bundlePtr
, CFStringRef nonLocalized
) {
214 CFStringRef localized
= NULL
;
215 CFBundleRef locBundle
= bundlePtr
? *bundlePtr
: NULL
;
217 CFURLRef url
= CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault
, CFSTR("/System/Library/CoreServices/SystemVersion.bundle"), kCFURLPOSIXPathStyle
, false);
219 locBundle
= CFBundleCreate(kCFAllocatorSystemDefault
, url
);
224 localized
= CFBundleCopyLocalizedString(locBundle
, nonLocalized
, nonLocalized
, CFSTR("SystemVersion"));
225 if (bundlePtr
) *bundlePtr
= locBundle
; else CFRelease(locBundle
);
227 return localized
? localized
: (CFStringRef
)CFRetain(nonLocalized
);
231 static CFDictionaryRef
_CFCopyVersionDictionary(CFStringRef path
) {
232 CFPropertyListRef plist
= NULL
;
236 url
= CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault
, path
, kCFURLPOSIXPathStyle
, false);
237 if (url
&& CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault
, url
, &data
, NULL
, NULL
, NULL
)) {
238 plist
= CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault
, data
, kCFPropertyListMutableContainers
, NULL
);
241 if (url
) CFRelease(url
);
244 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
245 CFBundleRef locBundle
= NULL
;
246 CFStringRef fullVersion
, vers
, versExtra
, build
;
247 CFStringRef versionString
= _CFCopyLocalizedVersionKey(&locBundle
, _kCFSystemVersionProductVersionStringKey
);
248 CFStringRef buildString
= _CFCopyLocalizedVersionKey(&locBundle
, _kCFSystemVersionBuildStringKey
);
249 CFStringRef fullVersionString
= _CFCopyLocalizedVersionKey(&locBundle
, CFSTR("FullVersionString"));
250 if (locBundle
) CFRelease(locBundle
);
252 // Now build the full version string
253 if (CFEqual(fullVersionString
, CFSTR("FullVersionString"))) {
254 CFRelease(fullVersionString
);
255 fullVersionString
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("%@ %%@ (%@ %%@)"), versionString
, buildString
);
257 vers
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)plist
, _kCFSystemVersionProductVersionKey
);
258 versExtra
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)plist
, _kCFSystemVersionProductVersionExtraKey
);
259 if (vers
&& versExtra
) vers
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("%@ %@"), vers
, versExtra
);
260 build
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)plist
, _kCFSystemVersionBuildVersionKey
);
261 fullVersion
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, fullVersionString
, (vers
? vers
: CFSTR("?")), build
? build
: CFSTR("?"));
262 if (vers
&& versExtra
) CFRelease(vers
);
264 CFDictionarySetValue((CFMutableDictionaryRef
)plist
, _kCFSystemVersionProductVersionStringKey
, versionString
);
265 CFDictionarySetValue((CFMutableDictionaryRef
)plist
, _kCFSystemVersionBuildStringKey
, buildString
);
266 CFDictionarySetValue((CFMutableDictionaryRef
)plist
, CFSTR("FullVersionString"), fullVersion
);
267 CFRelease(versionString
);
268 CFRelease(buildString
);
269 CFRelease(fullVersionString
);
270 CFRelease(fullVersion
);
273 return (CFDictionaryRef
)plist
;
276 CFStringRef
CFCopySystemVersionString(void) {
277 CFStringRef versionString
;
278 CFDictionaryRef dict
= _CFCopyServerVersionDictionary();
279 if (!dict
) dict
= _CFCopySystemVersionDictionary();
280 if (!dict
) return NULL
;
281 versionString
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("FullVersionString"));
282 if (versionString
) CFRetain(versionString
);
284 return versionString
;
287 // Obsolete: These two functions cache the dictionaries to avoid calling _CFCopyVersionDictionary() more than once per dict desired
288 // In fact, they do not cache any more, because the file can change after
289 // apps are running in some situations, and apps need the new info.
290 // Proper caching and testing to see if the file has changed, without race
291 // conditions, would require semi-convoluted use of fstat().
293 CFDictionaryRef
_CFCopySystemVersionDictionary(void) {
294 CFPropertyListRef plist
= NULL
;
295 plist
= _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/SystemVersion.plist"));
296 return (CFDictionaryRef
)plist
;
299 CFDictionaryRef
_CFCopyServerVersionDictionary(void) {
300 CFPropertyListRef plist
= NULL
;
301 plist
= _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/ServerVersion.plist"));
302 return (CFDictionaryRef
)plist
;
305 CONST_STRING_DECL(_kCFSystemVersionProductNameKey
, "ProductName")
306 CONST_STRING_DECL(_kCFSystemVersionProductCopyrightKey
, "ProductCopyright")
307 CONST_STRING_DECL(_kCFSystemVersionProductVersionKey
, "ProductVersion")
308 CONST_STRING_DECL(_kCFSystemVersionProductVersionExtraKey
, "ProductVersionExtra")
309 CONST_STRING_DECL(_kCFSystemVersionProductUserVisibleVersionKey
, "ProductUserVisibleVersion")
310 CONST_STRING_DECL(_kCFSystemVersionBuildVersionKey
, "ProductBuildVersion")
311 CONST_STRING_DECL(_kCFSystemVersionProductVersionStringKey
, "Version")
312 CONST_STRING_DECL(_kCFSystemVersionBuildStringKey
, "Build")
314 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || TARGET_IPHONE_SIMULATOR
317 uint16_t primaryVersion
;
318 uint8_t secondaryVersion
;
319 uint8_t tertiaryVersion
;
322 CFLibraryVersion
CFGetExecutableLinkedLibraryVersion(CFStringRef libraryName
) {
323 CFLibraryVersion ret
= {0xFFFF, 0xFF, 0xFF};
324 char library
[CFMaxPathSize
]; // search specs larger than this are pointless
325 if (!CFStringGetCString(libraryName
, library
, sizeof(library
), kCFStringEncodingUTF8
)) return ret
;
326 int32_t version
= NSVersionOfLinkTimeLibrary(library
);
328 ret
.primaryVersion
= version
>> 16;
329 ret
.secondaryVersion
= (version
>> 8) & 0xff;
330 ret
.tertiaryVersion
= version
& 0xff;
335 CFLibraryVersion
CFGetExecutingLibraryVersion(CFStringRef libraryName
) {
336 CFLibraryVersion ret
= {0xFFFF, 0xFF, 0xFF};
337 char library
[CFMaxPathSize
]; // search specs larger than this are pointless
338 if (!CFStringGetCString(libraryName
, library
, sizeof(library
), kCFStringEncodingUTF8
)) return ret
;
339 int32_t version
= NSVersionOfRunTimeLibrary(library
);
341 ret
.primaryVersion
= version
>> 16;
342 ret
.secondaryVersion
= (version
>> 8) & 0xff;
343 ret
.tertiaryVersion
= version
& 0xff;
348 static inline Boolean
_CFLibraryVersionLessThan(CFLibraryVersion vers1
, CFLibraryVersion vers2
) {
349 if (vers1
.primaryVersion
< vers2
.primaryVersion
) {
351 } else if (vers1
.primaryVersion
== vers2
.primaryVersion
) {
352 if (vers1
.secondaryVersion
< vers2
.secondaryVersion
) {
354 } else if (vers1
.secondaryVersion
== vers2
.secondaryVersion
) {
355 return vers1
.tertiaryVersion
< vers2
.tertiaryVersion
;
363 (vers != 0xFFFF): We know the version number of the library this app was linked against
364 and (versionInfo[version].VERSIONFIELD != 0xFFFF): And we know what version number started the specified release
365 and ((version == 0) || (versionInfo[version-1].VERSIONFIELD < versionInfo[version].VERSIONFIELD)): And it's distinct from the prev release
367 If the version the app is linked against is less than the version recorded for the specified release
368 Then stop checking and return false
369 Else stop checking and return YES
371 Continue checking (the next library)
374 #define resultIndex(VERSION) (VERSION)
376 #define checkLibrary(LIBNAME, VERSIONFIELD) { \
377 uint16_t vers = (NSVersionOfLinkTimeLibrary(LIBNAME) >> 16); \
378 if ((vers != 0xFFFF) && (versionInfo[version].VERSIONFIELD != 0xFFFF) && \
379 ((version == 0) || (versionInfo[version-1].VERSIONFIELD < versionInfo[version].VERSIONFIELD))) \
380 return (results[resultIndex(version)] = ((vers < versionInfo[version].VERSIONFIELD) ? false : true)); \
384 CF_EXPORT Boolean
_CFExecutableLinkedOnOrAfter(CFSystemVersion version
) {
385 // The numbers in the below tables should be the numbers for any version of the framework in the release.
386 // When adding new entries to these tables for a new build train, it's simplest to use the versions of the
387 // first new versions of projects submitted to the new train. These can later be updated. One thing to watch for is that software updates
388 // for the previous release do not increase numbers beyond the number used for the next release!
389 // For a given train, don't ever use the last versions submitted to the previous train! (This to assure room for software updates.)
390 // If versions are the same as previous release, use 0xFFFF; this will assure the answer is a conservative NO.
391 // NOTE: Also update the CFM check below, perhaps to the previous release... (???)
392 static const struct {
393 uint16_t libSystemVersion
;
394 uint16_t cocoaVersion
;
395 uint16_t appkitVersion
;
398 uint16_t carbonVersion
;
399 uint16_t applicationServicesVersion
;
400 uint16_t coreServicesVersion
;
401 uint16_t iokitVersion
;
403 {50, 5, 577, 397, 196, 113, 16, 9, 52}, /* CFSystemVersionCheetah (used the last versions) */
404 {55, 7, 620, 425, 226, 122, 16, 10, 67}, /* CFSystemVersionPuma (used the last versions) */
405 {56, 8, 631, 431, 232, 122, 17, 11, 73}, /* CFSystemVersionJaguar */
406 {67, 9, 704, 481, 281, 126, 19, 16, 159}, /* CFSystemVersionPanther */
407 {73, 10, 750, 505, 305, 128, 22, 18, 271}, /* CFSystemVersionTiger */
408 {89, 12, 840, 575, 375, 136, 34, 32, 0xFFFF}, /* CFSystemVersionLeopard */
409 {112, 13, 960, 680, 480, 0xFFFF, 0xFFFF, 33, 0xFFFF}, /* CFSystemVersionSnowLeopard */
413 // !!! When a new release is added to the array, don't forget to bump the size of this array!
414 static char results
[CFSystemVersionMax
] = {-2, -2, -2, -2, -2, -2}; /* We cache the results per-release; there are only a few of these... */
415 if (version
>= CFSystemVersionMax
) return false; /* Actually, we don't know the answer, and something scary is going on */
417 int versionIndex
= resultIndex(version
);
418 if (results
[versionIndex
] != -2) return results
[versionIndex
];
420 #if DEPLOYMENT_TARGET_MACOSX
422 results
[versionIndex
] = (version
<= CFSystemVersionJaguar
) ? true : false;
423 return results
[versionIndex
];
427 // Do a sanity check, since sometimes System framework is screwed up, which confuses everything.
428 // If the currently executing System framework has a version less than that of Leopard, warn.
429 static Boolean called
= false;
430 if (!called
) { // We do a check here in case CFLog() recursively calls this function.
432 int32_t vers
= NSVersionOfRunTimeLibrary("System");
433 if ((vers
!= -1) && (((unsigned int)vers
) >> 16) < 89) { // 89 is the version of libSystem for first version of Leopard
434 CFLog(__kCFLogAssertion
, CFSTR("System.framework version (%x) is wrong, this will break CF and up"), vers
);
436 if (results
[versionIndex
] != -2) return results
[versionIndex
]; // If there was a recursive call that figured this out, return
439 #if DEPLOYMENT_TARGET_MACOSX
440 if (version
< CFSystemVersionMax
) {
441 // Compare the linked library versions of a Mac OS X app to framework versions found on Mac OS X.
442 checkLibrary("System", libSystemVersion
); // Pretty much everyone links with this
443 checkLibrary("Cocoa", cocoaVersion
);
444 checkLibrary("AppKit", appkitVersion
);
445 checkLibrary("Foundation", fouVersion
);
446 checkLibrary("CoreFoundation", cfVersion
);
447 checkLibrary("Carbon", carbonVersion
);
448 checkLibrary("ApplicationServices", applicationServicesVersion
);
449 checkLibrary("CoreServices", coreServicesVersion
);
450 checkLibrary("IOKit", iokitVersion
);
456 /* If not found, then simply return NO to indicate earlier --- compatibility by default, unfortunately */
460 CF_EXPORT Boolean
_CFExecutableLinkedOnOrAfter(CFSystemVersion version
) {
466 #if DEPLOYMENT_TARGET_MACOSX
467 __private_extern__
void *__CFLookupCarbonCoreFunction(const char *name
) {
468 static void *image
= NULL
;
470 image
= dlopen("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", RTLD_LAZY
| RTLD_LOCAL
);
474 dyfunc
= dlsym(image
, name
);
480 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
481 __private_extern__
void *__CFLookupCFNetworkFunction(const char *name
) {
482 static void *image
= NULL
;
484 const char *path
= NULL
;
486 path
= __CFgetenv("CFNETWORK_LIBRARY_PATH");
489 #if DEPLOYMENT_TARGET_MACOSX
490 path
= "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CFNetwork.framework/Versions/A/CFNetwork";
492 path
= "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
495 image
= dlopen(path
, RTLD_LAZY
| RTLD_LOCAL
);
499 dyfunc
= dlsym(image
, name
);
506 #ifndef __CFGetSessionID_defined
508 __private_extern__
uint32_t __CFGetSessionID(void) {
514 __private_extern__ CFIndex
__CFActiveProcessorCount() {
516 #if DEPLOYMENT_TARGET_WINDOWS
518 GetSystemInfo(&sysInfo
);
519 DWORD_PTR activeProcessorMask
= sysInfo
.dwActiveProcessorMask
;
520 // assumes sizeof(DWORD_PTR) is 64 bits or less
521 uint64_t v
= activeProcessorMask
;
522 v
= v
- ((v
>> 1) & 0x5555555555555555ULL
);
523 v
= (v
& 0x3333333333333333ULL
) + ((v
>> 2) & 0x3333333333333333ULL
);
524 v
= (v
+ (v
>> 4)) & 0xf0f0f0f0f0f0f0fULL
;
525 pcnt
= (v
* 0x0101010101010101ULL
) >> ((sizeof(v
) - 1) * 8);
527 int32_t mib
[] = {CTL_HW
, HW_AVAILCPU
};
528 size_t len
= sizeof(pcnt
);
529 int32_t result
= sysctl(mib
, sizeof(mib
) / sizeof(int32_t), &pcnt
, &len
, NULL
, 0);
537 const char *_CFPrintForDebugger(const void *obj
) {
538 static char *result
= NULL
;
542 free(result
); // Let go of result from previous call.
545 if (CFGetTypeID(obj
) == CFStringGetTypeID()) {
546 // Makes Ali marginally happier
547 str
= __CFCopyFormattingDescription(obj
, NULL
);
548 if (!str
) str
= CFCopyDescription(obj
);
550 str
= CFCopyDescription(obj
);
553 str
= (CFStringRef
)CFRetain(CFSTR("(null)"));
557 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, 0, FALSE
, NULL
, 0, &cnt
);
559 result
= (char *) malloc(cnt
+ 2); // 1 for '\0', 1 for an optional '\n'
561 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, 0, FALSE
, (UInt8
*) result
, cnt
, &cnt
);
565 if (str
) CFRelease(str
);
569 static void _CFShowToFile(FILE *file
, Boolean flush
, const void *obj
) {
572 CFStringInlineBuffer buffer
;
576 if (CFGetTypeID(obj
) == CFStringGetTypeID()) {
577 // Makes Ali marginally happier
578 str
= __CFCopyFormattingDescription(obj
, NULL
);
579 if (!str
) str
= CFCopyDescription(obj
);
581 str
= CFCopyDescription(obj
);
584 str
= (CFStringRef
)CFRetain(CFSTR("(null)"));
586 cnt
= CFStringGetLength(str
);
588 // iTunes used OutputDebugStringW(theString);
590 CFStringInitInlineBuffer(str
, &buffer
, CFRangeMake(0, cnt
));
591 #if DEPLOYMENT_TARGET_WINDOWS
592 wchar_t *accumulatedBuffer
= (wchar_t *)malloc((cnt
+1) * sizeof(wchar_t));
594 for (idx
= 0; idx
< cnt
; idx
++) {
595 UniChar ch
= __CFStringGetCharacterFromInlineBufferQuick(&buffer
, idx
);
596 #if DEPLOYMENT_TARGET_WINDOWS
597 if (file
== stderr
|| file
== stdout
) {
598 accumulatedBuffer
[idx
] = ch
;
599 lastNL
= (ch
== L
'\n');
600 if (idx
== (cnt
- 1)) {
601 accumulatedBuffer
[idx
+1] = L
'\0';
602 OutputDebugStringW(accumulatedBuffer
);
603 free(accumulatedBuffer
);
608 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
610 fprintf_l(file
, NULL
, "%c", ch
);
611 lastNL
= (ch
== '\n');
613 fprintf_l(file
, NULL
, "\\u%04x", ch
);
615 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
617 _fprintf_l(file
, "%c", NULL
, ch
);
618 lastNL
= (ch
== '\n');
620 _fprintf_l(file
, "\\u%04x", NULL
, ch
);
624 #if DEPLOYMENT_TARGET_WINDOWS
628 #if DEPLOYMENT_TARGET_WINDOWS
629 if (file
== stderr
|| file
== stdout
) {
633 OutputDebugStringA(outStr
);
635 _fprintf_l(file
, "\n", NULL
);
636 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
637 fprintf_l(file
, NULL
, "\n");
639 #if DEPLOYMENT_TARGET_WINDOWS
642 if (flush
) fflush(file
);
645 if (str
) CFRelease(str
);
648 void CFShow(const void *obj
) {
649 _CFShowToFile(stderr
, true, obj
);
653 // message must be a UTF8-encoded, null-terminated, byte buffer with at least length bytes
654 typedef void (*CFLogFunc
)(int32_t lev
, const char *message
, size_t length
, char withBanner
);
656 static Boolean
also_do_stderr() {
657 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
658 if (!issetugid() && __CFgetenv("CFLOG_FORCE_STDERR")) {
662 int ret
= fstat(STDERR_FILENO
, &sb
);
663 if (ret
< 0) return false;
664 mode_t m
= sb
.st_mode
& S_IFMT
;
665 if (S_IFREG
== m
|| S_IFSOCK
== m
) return true;
666 if (!(S_IFIFO
== m
|| S_IFCHR
== m
)) return false; // disallow any whacky stuff
667 // if it could be a pipe back to launchd, fail
669 // assumes val is not written to on error
670 vproc_swap_integer(NULL
, VPROC_GSK_IS_MANAGED
, NULL
, &val
);
671 if (val
) return false;
676 static void __CFLogCString(int32_t lev
, const char *message
, size_t length
, char withBanner
) {
681 #if !(DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED)
685 CFAbsoluteTime at
= CFAbsoluteTimeGetCurrent();
686 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, kCFCalendarIdentifierGregorian
);
687 if (!calendar
) goto after_banner
;
688 CFTimeZoneRef tz
= CFTimeZoneCopySystem();
693 CFCalendarSetTimeZone(calendar
, tz
);
695 int32_t year
, month
, day
, hour
, minute
, second
;
696 Boolean dec
= CFCalendarDecomposeAbsoluteTime(calendar
, at
, "yMdHms", &year
, &month
, &day
, &hour
, &minute
, &second
);
698 if (!dec
) goto after_banner
;
700 int32_t ms
= (int32_t)floor(1000.0 * modf(at
, &atf
));
701 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
702 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()));
703 asprintf(&thread
, "%x", pthread_mach_thread_np(pthread_self()));
705 bannerLen
= asprintf(&banner
, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%x] ", year
, month
, day
, hour
, minute
, second
, ms
, *_CFGetProgname(), getpid(), GetCurrentThreadId());
706 asprintf(&thread
, "%x", GetCurrentThreadId());
708 asprintf(&time
, "%04d-%02d-%02d %02d:%02d:%02d.%03d", year
, month
, day
, hour
, minute
, second
, ms
);
712 asprintf(&uid
, "%d", geteuid());
713 aslclient asl
= asl_open(NULL
, "com.apple.console", ASL_OPT_NO_DELAY
);
714 aslmsg msg
= asl_new(ASL_TYPE_MSG
);
715 asl_set(msg
, "CFLog Local Time", time
); // not to be documented, not public API
716 asl_set(msg
, "CFLog Thread", thread
); // not to be documented, not public API
717 asl_set(msg
, "ReadUID", uid
);
718 static const char *levstr
[] = {"0", "1", "2", "3", "4", "5", "6", "7"};
719 asl_set(msg
, ASL_KEY_LEVEL
, levstr
[lev
]);
720 asl_set(msg
, ASL_KEY_MSG
, message
);
725 if (also_do_stderr()) {
726 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
728 v
[0].iov_base
= banner
;
729 v
[0].iov_len
= banner
? strlen(banner
) : 0;
730 v
[1].iov_base
= (char *)message
;
731 v
[1].iov_len
= length
;
732 v
[2].iov_base
= "\n";
733 v
[2].iov_len
= (message
[length
- 1] != '\n') ? 1 : 0;
734 int nv
= (v
[0].iov_base
? 1 : 0) + 1 + (v
[2].iov_len
? 1 : 0);
735 static CFSpinLock_t lock
= CFSpinLockInit
;
737 writev(STDERR_FILENO
, v
[0].iov_base
? v
: v
+ 1, nv
);
738 __CFSpinUnlock(&lock
);
740 size_t bufLen
= bannerLen
+ length
+ 1;
741 char *buf
= (char *)malloc(sizeof(char) * bufLen
);
743 // Copy the banner into the debug string
744 memmove_s(buf
, bufLen
, banner
, bannerLen
);
746 // Copy the message into the debug string
747 strcpy_s(buf
+ bannerLen
, bufLen
- bannerLen
, message
);
749 strcpy_s(buf
, bufLen
, message
);
751 buf
[bufLen
- 1] = '\0';
752 fprintf_s(stderr
, "%s\n", buf
);
753 // This Win32 API call only prints when a debugger is active
754 // OutputDebugStringA(buf);
759 if (thread
) free(thread
);
760 if (time
) free(time
);
761 if (banner
) free(banner
);
765 CF_EXPORT
void _CFLogvEx(CFLogFunc logit
, CFStringRef (*copyDescFunc
)(void *, const void *), CFDictionaryRef formatOptions
, int32_t lev
, CFStringRef format
, va_list args
) {
766 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
767 if (pthread_getspecific(__CFTSDKeyIsInCFLog
)) return;
768 pthread_setspecific(__CFTSDKeyIsInCFLog
, (void *)1);
770 CFStringRef str
= format
? _CFStringCreateWithFormatAndArgumentsAux(kCFAllocatorSystemDefault
, copyDescFunc
, formatOptions
, (CFStringRef
)format
, args
) : 0;
771 CFIndex blen
= str
? CFStringGetMaximumSizeForEncoding(CFStringGetLength(str
), kCFStringEncodingUTF8
) + 1 : 0;
772 char *buf
= str
? (char *)malloc(blen
) : 0;
774 Boolean converted
= CFStringGetCString(str
, buf
, blen
, kCFStringEncodingUTF8
);
775 size_t len
= strlen(buf
);
776 // silently ignore 0-length or really large messages, and levels outside the valid range
777 if (converted
&& !(len
<= 0 || (1 << 24) < len
) && !(lev
< ASL_LEVEL_EMERG
|| ASL_LEVEL_DEBUG
< lev
)) {
778 (logit
? logit
: __CFLogCString
)(lev
, buf
, len
, 1);
782 if (str
) CFRelease(str
);
783 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
784 pthread_setspecific(__CFTSDKeyIsInCFLog
, 0);
788 void CFLog(int32_t lev
, CFStringRef format
, ...) {
790 va_start(args
, format
);
791 _CFLogvEx(NULL
, NULL
, NULL
, lev
, format
, args
);
797 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
799 kern_return_t
_CFDiscorporateMemoryAllocate(CFDiscorporateMemory
*hm
, size_t size
, bool purgeable
) {
800 kern_return_t ret
= KERN_SUCCESS
;
801 size
= round_page(size
);
802 if (0 == size
) size
= vm_page_size
;
803 memset(hm
, 0, sizeof(CFDiscorporateMemory
));
804 void *addr
= mmap(0, size
, PROT_READ
| PROT_WRITE
, MAP_ANON
| MAP_PRIVATE
, VM_MAKE_TAG(0) | (purgeable
? VM_FLAGS_PURGABLE
: 0), 0);
805 if ((uintptr_t)addr
== -1) {
808 if (KERN_SUCCESS
== ret
) {
809 hm
->address
= (mach_vm_address_t
)(uintptr_t)addr
;
810 hm
->size
= (mach_vm_size_t
)size
;
811 hm
->port
= MACH_PORT_NULL
;
812 hm
->corporeal
= true;
813 hm
->purgeable
= purgeable
;
815 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
);
816 if (KERN_SUCCESS
== ret
) hm
->corporeal
= true;
820 kern_return_t
_CFDiscorporateMemoryDeallocate(CFDiscorporateMemory
*hm
) {
821 kern_return_t ret1
= KERN_SUCCESS
, ret2
= KERN_SUCCESS
;
822 if (hm
->corporeal
) ret1
= mach_vm_deallocate(mach_task_self(), hm
->address
, hm
->size
);
823 hm
->address
= MACH_VM_MIN_ADDRESS
;
824 hm
->corporeal
= false;
825 ret2
= mach_port_deallocate(mach_task_self(), hm
->port
);
826 hm
->port
= MACH_PORT_NULL
;
827 return ret1
!= KERN_SUCCESS
? ret1
: ret2
;
830 kern_return_t
_CFDiscorporateMemoryDematerialize(CFDiscorporateMemory
*hm
) {
831 kern_return_t ret
= KERN_SUCCESS
;
832 if (!hm
->corporeal
) ret
= KERN_INVALID_MEMORY_CONTROL
;
833 int state
= VM_PURGABLE_VOLATILE
;
834 if (KERN_SUCCESS
== ret
) vm_purgable_control(mach_task_self(), (vm_address_t
)hm
->address
, VM_PURGABLE_SET_STATE
, &state
);
835 if (KERN_SUCCESS
== ret
) ret
= mach_vm_deallocate(mach_task_self(), hm
->address
, hm
->size
);
836 if (KERN_SUCCESS
== ret
) hm
->address
= MACH_VM_MIN_ADDRESS
;
837 if (KERN_SUCCESS
== ret
) hm
->corporeal
= false;
841 kern_return_t
_CFDiscorporateMemoryMaterialize(CFDiscorporateMemory
*hm
) {
842 kern_return_t ret
= KERN_SUCCESS
;
843 if (hm
->corporeal
) ret
= KERN_INVALID_MEMORY_CONTROL
;
844 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
);
845 if (KERN_SUCCESS
== ret
) hm
->corporeal
= true;
846 int state
= VM_PURGABLE_NONVOLATILE
;
847 if (KERN_SUCCESS
== ret
) ret
= vm_purgable_control(mach_task_self(), (vm_address_t
)hm
->address
, VM_PURGABLE_SET_STATE
, &state
);
848 if (KERN_SUCCESS
== ret
) if (VM_PURGABLE_EMPTY
== state
) ret
= KERN_PROTECTION_FAILURE
; // same as VM_PURGABLE_EMPTY
854 #if DEPLOYMENT_TARGET_MACOSX
856 #define SUDDEN_TERMINATION_ENABLE_VPROC 1
858 #if SUDDEN_TERMINATION_ENABLE_VPROC
860 static CFSpinLock_t __CFProcessKillingLock
= CFSpinLockInit
;
861 static CFIndex __CFProcessKillingDisablingCount
= 1;
862 static Boolean __CFProcessKillingWasTurnedOn
= false;
864 void _CFSuddenTerminationDisable(void) {
865 __CFSpinLock(&__CFProcessKillingLock
);
866 __CFProcessKillingDisablingCount
++;
867 _vproc_transaction_begin();
868 __CFSpinUnlock(&__CFProcessKillingLock
);
871 void _CFSuddenTerminationEnable(void) {
872 // 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.
873 __CFSpinLock(&__CFProcessKillingLock
);
874 __CFProcessKillingDisablingCount
--;
875 if (__CFProcessKillingDisablingCount
==0 && !__CFProcessKillingWasTurnedOn
) {
876 int64_t transactionsAreToBeEnabled
= 1;
877 int64_t transactionsWereAlreadyEnabled
= 0;
878 vproc_err_t verr
= vproc_swap_integer(NULL
, VPROC_GSK_TRANSACTIONS_ENABLED
, &transactionsAreToBeEnabled
, &transactionsWereAlreadyEnabled
);
880 if (!transactionsWereAlreadyEnabled
) {
881 // We set __CFProcessKillingWasTurnedOn below regardless of success because there's no point in retrying.
882 } // else this process was launched by launchd with transactions already enabled because EnableTransactions was set to true in the launchd .plist file.
883 } // else this process was not launched by launchd and the fix for 6416724 is not in the build yet.
884 __CFProcessKillingWasTurnedOn
= true;
886 // 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.
887 if (__CFProcessKillingDisablingCount
>=0) {
888 _vproc_transaction_end();
890 CFLog(kCFLogLevelError
, CFSTR("-[NSProcessInfo enableSuddenTermination] has been invoked more times than necessary to balance invocations of -[NSProcessInfo disableSuddenTermination]. Ignoring."));
893 __CFSpinUnlock(&__CFProcessKillingLock
);
896 void _CFSuddenTerminationExitIfTerminationEnabled(int exitStatus
) {
897 // 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.
898 __CFSpinLock(&__CFProcessKillingLock
);
899 // 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.
900 if (_vproc_transaction_count()==0 && __CFProcessKillingWasTurnedOn
) {
903 __CFSpinUnlock(&__CFProcessKillingLock
);
906 void _CFSuddenTerminationExitWhenTerminationEnabled(int exitStatus
) {
907 // The user has had their final opportunity to cancel quitting. Exit as soon as the process is clean. Same carefulness as in _CFSuddenTerminationExitIfTerminationEnabled().
908 __CFSpinLock(&__CFProcessKillingLock
);
909 if (__CFProcessKillingWasTurnedOn
) {
910 _vproc_transaction_try_exit(exitStatus
);
912 __CFSpinUnlock(&__CFProcessKillingLock
);
915 size_t _CFSuddenTerminationDisablingCount(void) {
916 // 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.
917 return _vproc_transaction_count() + (__CFProcessKillingWasTurnedOn
? 0 : 1);
922 #warning Building with vproc sudden termination API disabled.
924 static CFSpinLock_t __CFProcessKillingLock
= CFSpinLockInit
;
925 static size_t __CFProcessKillingDisablingCount
= 1;
926 static Boolean __CFProcessExitNextTimeKillingIsEnabled
= false;
927 static int32_t __CFProcessExitStatus
= 0;
928 static int __CFProcessIsKillableNotifyToken
;
929 static Boolean __CFProcessIsKillableNotifyTokenIsFigured
= false;
931 __private_extern__
void _CFSetSuddenTerminationEnabled(Boolean isEnabled
) {
932 if (!__CFProcessIsKillableNotifyTokenIsFigured
) {
933 char *notificationName
= NULL
;
934 asprintf(¬ificationName
, "com.apple.isKillable.%i", getpid());
935 uint32_t notifyResult
= notify_register_check(notificationName
, &__CFProcessIsKillableNotifyToken
);
936 if (notifyResult
!= NOTIFY_STATUS_OK
) {
937 CFLog(kCFLogLevelError
, CFSTR("%s: notify_register_check() returned %i."), __PRETTY_FUNCTION__
, notifyResult
);
939 free(notificationName
);
940 __CFProcessIsKillableNotifyTokenIsFigured
= true;
942 uint32_t notifyResult
= notify_set_state(__CFProcessIsKillableNotifyToken
, isEnabled
);
943 if (notifyResult
!= NOTIFY_STATUS_OK
) {
944 CFLog(kCFLogLevelError
, CFSTR("%s: notify_set_state() returned %i"), __PRETTY_FUNCTION__
, notifyResult
);
948 void _CFSuddenTerminationDisable(void) {
949 __CFSpinLock(&__CFProcessKillingLock
);
950 if (__CFProcessKillingDisablingCount
== 0) {
951 _CFSetSuddenTerminationEnabled(false);
953 __CFProcessKillingDisablingCount
++;
954 __CFSpinUnlock(&__CFProcessKillingLock
);
957 void _CFSuddenTerminationEnable(void) {
958 __CFSpinLock(&__CFProcessKillingLock
);
959 __CFProcessKillingDisablingCount
--;
960 if (__CFProcessKillingDisablingCount
== 0) {
961 if (__CFProcessExitNextTimeKillingIsEnabled
) {
962 _exit(__CFProcessExitStatus
);
964 _CFSetSuddenTerminationEnabled(true);
967 __CFSpinUnlock(&__CFProcessKillingLock
);
970 void _CFSuddenTerminationExitIfTerminationEnabled(int exitStatus
) {
971 __CFSpinLock(&__CFProcessKillingLock
);
972 if (__CFProcessKillingDisablingCount
== 0) {
975 __CFSpinUnlock(&__CFProcessKillingLock
);
978 void _CFSuddenTerminationExitWhenTerminationEnabled(int exitStatus
) {
979 __CFSpinLock(&__CFProcessKillingLock
);
980 if (__CFProcessKillingDisablingCount
== 0) {
983 __CFProcessExitNextTimeKillingIsEnabled
= YES
;
984 __CFProcessExitStatus
= exitStatus
;
986 __CFSpinUnlock(&__CFProcessKillingLock
);
989 size_t _CFSuddenTerminationDisablingCount(void) {
990 return __CFProcessKillingDisablingCount
;
998 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
1000 typedef void (^ThrottleTypeA
)(void); // allows calls per nanoseconds
1001 typedef void (^ThrottleTypeB
)(uint64_t amt
); // allows amount per nanoseconds
1003 __private_extern__ ThrottleTypeA
__CFCreateThrottleTypeA(uint16_t calls
, uint64_t nanoseconds
) {
1004 struct mach_timebase_info info
;
1005 mach_timebase_info(&info
);
1006 uint64_t period
= nanoseconds
/ info
.numer
* info
.denom
;
1008 if (0 == calls
|| 0 == period
) return NULL
;
1010 __block OSSpinLock b_lock
= OS_SPINLOCK_INIT
;
1011 __block
uint64_t b_values
[calls
];
1012 __block
uint64_t *b_oldest
= b_values
;
1013 memset(b_values
, 0, sizeof(b_values
));
1015 return Block_copy(^{
1016 uint64_t curr_time
= mach_absolute_time();
1017 OSSpinLockLock(&b_lock
);
1018 uint64_t next_time
= *b_oldest
+ period
;
1019 *b_oldest
= (curr_time
< next_time
) ? next_time
: curr_time
;
1021 if (b_values
+ calls
<= b_oldest
) b_oldest
= b_values
;
1022 OSSpinLockUnlock(&b_lock
);
1023 if (curr_time
< next_time
) {
1024 mach_wait_until(next_time
);
1029 __private_extern__ ThrottleTypeB
__CFCreateThrottleTypeB(uint64_t amount
, uint64_t nanoseconds
) {
1030 struct mach_timebase_info info
;
1031 mach_timebase_info(&info
);
1032 uint64_t period
= nanoseconds
/ info
.numer
* info
.denom
;
1034 if (0 == amount
|| 0 == period
) return NULL
;
1036 __block OSSpinLock b_lock
= OS_SPINLOCK_INIT
;
1037 __block
uint64_t b_sum
= 0ULL;
1038 __block
uint16_t b_num_values
= 8;
1039 __block
uint64_t *b_values
= calloc(b_num_values
, 2 * sizeof(uint64_t));
1040 __block
uint64_t *b_oldest
= b_values
;
1042 return Block_copy(^(uint64_t amt
){
1043 OSSpinLockLock(&b_lock
);
1045 OSSpinLockUnlock(&b_lock
);