]> git.saurik.com Git - apple/cf.git/blob - CFUtilities.c
CF-635.tar.gz
[apple/cf.git] / CFUtilities.c
1 /*
2 * Copyright (c) 2011 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-2011, 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
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 #endif
79
80 /* Comparator is passed the address of the values. */
81 /* Binary searches a sorted-increasing array of some type.
82 Return value is either 1) the index of the element desired,
83 if the target value exists in the list, 2) greater than or
84 equal to count, if the element is greater than all the values
85 in the list, or 3) the index of the element greater than the
86 target value.
87
88 For example, a search in the list of integers:
89 2 3 5 7 11 13 17
90
91 For... Will Return...
92 2 0
93 5 2
94 23 7
95 1 0
96 9 4
97
98 For instance, if you just care about found/not found:
99 index = CFBSearch(list, count, elem);
100 if (count <= index || list[index] != elem) {
101 * Not found *
102 } else {
103 * Found *
104 }
105
106 */
107 __private_extern__ CFIndex CFBSearch(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) {
108 const char *ptr = (const char *)list;
109 while (0 < count) {
110 CFIndex half = count / 2;
111 const char *probe = ptr + elementSize * half;
112 CFComparisonResult cr = comparator(element, probe, context);
113 if (0 == cr) return (probe - (const char *)list) / elementSize;
114 ptr = (cr < 0) ? ptr : probe + elementSize;
115 count = (cr < 0) ? half : (half + (count & 1) - 1);
116 }
117 return (ptr - (const char *)list) / elementSize;
118 }
119
120
121 #define ELF_STEP(B) T1 = (H << 4) + B; T2 = T1 & 0xF0000000; if (T2) T1 ^= (T2 >> 24); T1 &= (~T2); H = T1;
122
123 CFHashCode CFHashBytes(uint8_t *bytes, CFIndex length) {
124 /* The ELF hash algorithm, used in the ELF object file format */
125 UInt32 H = 0, T1, T2;
126 SInt32 rem = length;
127 while (3 < rem) {
128 ELF_STEP(bytes[length - rem]);
129 ELF_STEP(bytes[length - rem + 1]);
130 ELF_STEP(bytes[length - rem + 2]);
131 ELF_STEP(bytes[length - rem + 3]);
132 rem -= 4;
133 }
134 switch (rem) {
135 case 3: ELF_STEP(bytes[length - 3]);
136 case 2: ELF_STEP(bytes[length - 2]);
137 case 1: ELF_STEP(bytes[length - 1]);
138 case 0: ;
139 }
140 return H;
141 }
142
143 #undef ELF_STEP
144
145
146 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
147 __private_extern__ uintptr_t __CFFindPointer(uintptr_t ptr, uintptr_t start) {
148 vm_map_t task = mach_task_self();
149 mach_vm_address_t address = start;
150 for (;;) {
151 mach_vm_size_t size = 0;
152 vm_region_basic_info_data_64_t info;
153 mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
154 mach_port_t object_name;
155 kern_return_t ret = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &count, &object_name);
156 if (KERN_SUCCESS != ret) break;
157 boolean_t scan = (info.protection & VM_PROT_WRITE) ? 1 : 0;
158 if (scan) {
159 uintptr_t *addr = (uintptr_t *)((uintptr_t)address);
160 uintptr_t *end = (uintptr_t *)((uintptr_t)address + (uintptr_t)size);
161 while (addr < end) {
162 if ((uintptr_t *)start <= addr && *addr == ptr) {
163 return (uintptr_t)addr;
164 }
165 addr++;
166 }
167 }
168 address += size;
169 }
170 return 0;
171 }
172
173 __private_extern__ void __CFDumpAllPointerLocations(uintptr_t ptr) {
174 uintptr_t addr = 0;
175 do {
176 addr = __CFFindPointer(ptr, sizeof(void *) + addr);
177 printf("%p\n", (void *)addr);
178 } while (addr != 0);
179 }
180 #endif
181
182 #if DEPLOYMENT_TARGET_WINDOWS
183 struct _args {
184 void *func;
185 void *arg;
186 HANDLE handle;
187 };
188 static unsigned __stdcall __CFWinThreadFunc(void *arg) {
189 struct _args *args = (struct _args*)arg;
190 ((void (*)(void *))args->func)(args->arg);
191 CloseHandle(args->handle);
192 CFAllocatorDeallocate(kCFAllocatorSystemDefault, arg);
193 _endthreadex(0);
194 return 0;
195 }
196 #endif
197
198 __private_extern__ void *__CFStartSimpleThread(void *func, void *arg) {
199 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
200 pthread_attr_t attr;
201 pthread_t tid = 0;
202 pthread_attr_init(&attr);
203 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
204 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
205 pthread_attr_setstacksize(&attr, 60 * 1024); // 60K stack for our internal threads is sufficient
206 OSMemoryBarrier(); // ensure arg is fully initialized and set in memory
207 pthread_create(&tid, &attr, func, arg);
208 pthread_attr_destroy(&attr);
209 //warning CF: we dont actually know that a pthread_t is the same size as void *
210 return (void *)tid;
211 #elif DEPLOYMENT_TARGET_WINDOWS
212 unsigned tid;
213 struct _args *args = (struct _args*)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(struct _args), 0);
214 if (__CFOASafe) __CFSetLastAllocationEventName(args, "CFUtilities (thread-args)");
215 HANDLE handle;
216 args->func = func;
217 args->arg = arg;
218 /* 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. */
219 args->handle = (HANDLE)_beginthreadex(NULL, 0, __CFWinThreadFunc, args, CREATE_SUSPENDED, &tid);
220 handle = args->handle;
221 ResumeThread(handle);
222 return handle;
223 #endif
224 }
225
226
227 // Looks for localized version of "nonLocalized" in the SystemVersion bundle
228 // If not found, and returnNonLocalizedFlag == true, will return the non localized string (retained of course), otherwise NULL
229 // If bundlePtr != NULL, will use *bundlePtr and will return the bundle in there; otherwise bundle is created and released
230 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
231 static CFStringRef _CFCopyLocalizedVersionKey(CFBundleRef *bundlePtr, CFStringRef nonLocalized) {
232 CFStringRef localized = NULL;
233 CFBundleRef locBundle = bundlePtr ? *bundlePtr : NULL;
234 if (!locBundle) {
235 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, CFSTR("/System/Library/CoreServices/SystemVersion.bundle"), kCFURLPOSIXPathStyle, false);
236 if (url) {
237 locBundle = CFBundleCreate(kCFAllocatorSystemDefault, url);
238 CFRelease(url);
239 }
240 }
241 if (locBundle) {
242 localized = CFBundleCopyLocalizedString(locBundle, nonLocalized, nonLocalized, CFSTR("SystemVersion"));
243 if (bundlePtr) *bundlePtr = locBundle; else CFRelease(locBundle);
244 }
245 return localized ? localized : (CFStringRef)CFRetain(nonLocalized);
246 }
247 #endif
248
249 static CFDictionaryRef _CFCopyVersionDictionary(CFStringRef path) {
250 CFPropertyListRef plist = NULL;
251 CFDataRef data;
252 CFURLRef url;
253
254 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, path, kCFURLPOSIXPathStyle, false);
255 if (url && CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, url, &data, NULL, NULL, NULL)) {
256 plist = CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListMutableContainers, NULL);
257 CFRelease(data);
258 }
259 if (url) CFRelease(url);
260
261 if (plist) {
262 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
263 CFBundleRef locBundle = NULL;
264 CFStringRef fullVersion, vers, versExtra, build;
265 CFStringRef versionString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionProductVersionStringKey);
266 CFStringRef buildString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionBuildStringKey);
267 CFStringRef fullVersionString = _CFCopyLocalizedVersionKey(&locBundle, CFSTR("FullVersionString"));
268 if (locBundle) CFRelease(locBundle);
269
270 // Now build the full version string
271 if (CFEqual(fullVersionString, CFSTR("FullVersionString"))) {
272 CFRelease(fullVersionString);
273 fullVersionString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@ %%@ (%@ %%@)"), versionString, buildString);
274 }
275 vers = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionProductVersionKey);
276 versExtra = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionProductVersionExtraKey);
277 if (vers && versExtra) vers = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@ %@"), vers, versExtra);
278 build = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionBuildVersionKey);
279 fullVersion = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, fullVersionString, (vers ? vers : CFSTR("?")), build ? build : CFSTR("?"));
280 if (vers && versExtra) CFRelease(vers);
281
282 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductVersionStringKey, versionString);
283 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionBuildStringKey, buildString);
284 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR("FullVersionString"), fullVersion);
285 CFRelease(versionString);
286 CFRelease(buildString);
287 CFRelease(fullVersionString);
288 CFRelease(fullVersion);
289 #endif
290 }
291 return (CFDictionaryRef)plist;
292 }
293
294 CFStringRef CFCopySystemVersionString(void) {
295 CFStringRef versionString;
296 CFDictionaryRef dict = _CFCopyServerVersionDictionary();
297 if (!dict) dict = _CFCopySystemVersionDictionary();
298 if (!dict) return NULL;
299 versionString = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("FullVersionString"));
300 if (versionString) CFRetain(versionString);
301 CFRelease(dict);
302 return versionString;
303 }
304
305 // Obsolete: These two functions cache the dictionaries to avoid calling _CFCopyVersionDictionary() more than once per dict desired
306 // In fact, they do not cache any more, because the file can change after
307 // apps are running in some situations, and apps need the new info.
308 // Proper caching and testing to see if the file has changed, without race
309 // conditions, would require semi-convoluted use of fstat().
310
311 CFDictionaryRef _CFCopySystemVersionDictionary(void) {
312 CFPropertyListRef plist = NULL;
313 plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/SystemVersion.plist"));
314 return (CFDictionaryRef)plist;
315 }
316
317 CFDictionaryRef _CFCopyServerVersionDictionary(void) {
318 CFPropertyListRef plist = NULL;
319 plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/ServerVersion.plist"));
320 return (CFDictionaryRef)plist;
321 }
322
323 CONST_STRING_DECL(_kCFSystemVersionProductNameKey, "ProductName")
324 CONST_STRING_DECL(_kCFSystemVersionProductCopyrightKey, "ProductCopyright")
325 CONST_STRING_DECL(_kCFSystemVersionProductVersionKey, "ProductVersion")
326 CONST_STRING_DECL(_kCFSystemVersionProductVersionExtraKey, "ProductVersionExtra")
327 CONST_STRING_DECL(_kCFSystemVersionProductUserVisibleVersionKey, "ProductUserVisibleVersion")
328 CONST_STRING_DECL(_kCFSystemVersionBuildVersionKey, "ProductBuildVersion")
329 CONST_STRING_DECL(_kCFSystemVersionProductVersionStringKey, "Version")
330 CONST_STRING_DECL(_kCFSystemVersionBuildStringKey, "Build")
331
332
333 CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version) {
334 return true;
335 }
336
337
338
339
340 #if DEPLOYMENT_TARGET_MACOSX
341 __private_extern__ void *__CFLookupCarbonCoreFunction(const char *name) {
342 static void *image = NULL;
343 if (NULL == image) {
344 image = dlopen("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", RTLD_LAZY | RTLD_LOCAL);
345 }
346 void *dyfunc = NULL;
347 if (image) {
348 dyfunc = dlsym(image, name);
349 }
350 return dyfunc;
351 }
352 #endif
353
354 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
355 __private_extern__ void *__CFLookupCoreServicesInternalFunction(const char *name) {
356 static void *image = NULL;
357 if (NULL == image) {
358 image = dlopen("/System/Library/PrivateFrameworks/CoreServicesInternal.framework/CoreServicesInternal", RTLD_LAZY | RTLD_LOCAL);
359 }
360 void *dyfunc = NULL;
361 if (image) {
362 dyfunc = dlsym(image, name);
363 }
364 return dyfunc;
365 }
366 #endif
367
368 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
369 __private_extern__ void *__CFLookupCFNetworkFunction(const char *name) {
370 static void *image = NULL;
371 if (NULL == image) {
372 const char *path = NULL;
373 if (!issetugid()) {
374 path = __CFgetenv("CFNETWORK_LIBRARY_PATH");
375 }
376 if (!path) {
377 #if DEPLOYMENT_TARGET_MACOSX
378 path = "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CFNetwork.framework/Versions/A/CFNetwork";
379 #else
380 path = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
381 #endif
382 }
383 image = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
384 }
385 void *dyfunc = NULL;
386 if (image) {
387 dyfunc = dlsym(image, name);
388 }
389 return dyfunc;
390 }
391 #endif
392
393
394 #ifndef __CFGetSessionID_defined
395
396 __private_extern__ uint32_t __CFGetSessionID(void) {
397 return 0;
398 }
399
400 #endif
401
402 __private_extern__ CFIndex __CFActiveProcessorCount() {
403 int32_t pcnt;
404 #if DEPLOYMENT_TARGET_WINDOWS
405 SYSTEM_INFO sysInfo;
406 GetSystemInfo(&sysInfo);
407 DWORD_PTR activeProcessorMask = sysInfo.dwActiveProcessorMask;
408 // assumes sizeof(DWORD_PTR) is 64 bits or less
409 uint64_t v = activeProcessorMask;
410 v = v - ((v >> 1) & 0x5555555555555555ULL);
411 v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
412 v = (v + (v >> 4)) & 0xf0f0f0f0f0f0f0fULL;
413 pcnt = (v * 0x0101010101010101ULL) >> ((sizeof(v) - 1) * 8);
414 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
415 int32_t mib[] = {CTL_HW, HW_AVAILCPU};
416 size_t len = sizeof(pcnt);
417 int32_t result = sysctl(mib, sizeof(mib) / sizeof(int32_t), &pcnt, &len, NULL, 0);
418 if (result != 0) {
419 pcnt = 0;
420 }
421 #else
422 // Assume the worst
423 pcnt = 1;
424 #endif
425 return pcnt;
426 }
427
428 const char *_CFPrintForDebugger(const void *obj) {
429 static char *result = NULL;
430 CFStringRef str;
431 CFIndex cnt = 0;
432
433 free(result); // Let go of result from previous call.
434 result = NULL;
435 if (obj) {
436 if (CFGetTypeID(obj) == CFStringGetTypeID()) {
437 // Makes Ali marginally happier
438 str = __CFCopyFormattingDescription(obj, NULL);
439 if (!str) str = CFCopyDescription(obj);
440 } else {
441 str = CFCopyDescription(obj);
442 }
443 } else {
444 str = (CFStringRef)CFRetain(CFSTR("(null)"));
445 }
446
447 if (str != NULL) {
448 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, FALSE, NULL, 0, &cnt);
449 }
450 result = (char *) malloc(cnt + 2); // 1 for '\0', 1 for an optional '\n'
451 if (str != NULL) {
452 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, FALSE, (UInt8 *) result, cnt, &cnt);
453 }
454 result[cnt] = '\0';
455
456 if (str) CFRelease(str);
457 return result;
458 }
459
460 static void _CFShowToFile(FILE *file, Boolean flush, const void *obj) {
461 CFStringRef str;
462 CFIndex idx, cnt;
463 CFStringInlineBuffer buffer;
464 bool lastNL = false;
465
466 if (obj) {
467 if (CFGetTypeID(obj) == CFStringGetTypeID()) {
468 // Makes Ali marginally happier
469 str = __CFCopyFormattingDescription(obj, NULL);
470 if (!str) str = CFCopyDescription(obj);
471 } else {
472 str = CFCopyDescription(obj);
473 }
474 } else {
475 str = (CFStringRef)CFRetain(CFSTR("(null)"));
476 }
477 cnt = CFStringGetLength(str);
478
479 #if DEPLOYMENT_TARGET_WINDOWS
480 UniChar *ptr = (UniChar *)CFStringGetCharactersPtr(str);
481 BOOL freePtr = false;
482 if (!ptr) {
483 CFIndex strLen = CFStringGetLength(str);
484 // +2, 1 for newline, 1 for null
485 CFIndex bufSize = sizeof(UniChar *) * (CFStringGetMaximumSizeForEncoding(strLen, kCFStringEncodingUnicode) + 2);
486 CFIndex bytesUsed = 0;
487 ptr = (UniChar *)malloc(bufSize);
488 CFStringGetCharacters(str, CFRangeMake(0, strLen), ptr);
489 ptr[strLen] = L'\n';
490 ptr[strLen+1] = 0;
491 freePtr = true;
492 }
493 OutputDebugStringW((wchar_t *)ptr);
494 if (freePtr) free(ptr);
495 #else
496 CFStringInitInlineBuffer(str, &buffer, CFRangeMake(0, cnt));
497 for (idx = 0; idx < cnt; idx++) {
498 UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&buffer, idx);
499 if (ch < 128) {
500 fprintf_l(file, NULL, "%c", ch);
501 lastNL = (ch == '\n');
502 } else {
503 fprintf_l(file, NULL, "\\u%04x", ch);
504 }
505 }
506 if (!lastNL) {
507 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
508 fprintf_l(file, NULL, "\n");
509 #else
510 fprintf(file, NULL, "\n");
511 #endif
512 if (flush) fflush(file);
513 }
514 #endif
515
516 if (str) CFRelease(str);
517 }
518
519 void CFShow(const void *obj) {
520 _CFShowToFile(stderr, true, obj);
521 }
522
523
524 // message must be a UTF8-encoded, null-terminated, byte buffer with at least length bytes
525 typedef void (*CFLogFunc)(int32_t lev, const char *message, size_t length, char withBanner);
526
527 static Boolean also_do_stderr() {
528 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
529 if (!issetugid() && __CFgetenv("CFLOG_FORCE_STDERR")) {
530 return true;
531 }
532 struct stat sb;
533 int ret = fstat(STDERR_FILENO, &sb);
534 if (ret < 0) return false;
535 mode_t m = sb.st_mode & S_IFMT;
536 if (S_IFREG == m || S_IFSOCK == m) return true;
537 if (!(S_IFIFO == m || S_IFCHR == m)) return false; // disallow any whacky stuff
538 // if it could be a pipe back to launchd, fail
539 int64_t val = 0;
540 // assumes val is not written to on error
541 vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &val);
542 if (val) return false;
543 #endif
544 return true;
545 }
546
547 extern char *__CFBundleMainID;
548
549 static void __CFLogCString(int32_t lev, const char *message, size_t length, char withBanner) {
550 char *banner = NULL;
551 char *time = NULL;
552 char *thread = NULL;
553 char *uid = NULL;
554 #if !(DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED)
555 int bannerLen = 0;
556 #endif
557 // 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.
558 if (withBanner) {
559 CFAbsoluteTime at = CFAbsoluteTimeGetCurrent();
560 CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFCalendarIdentifierGregorian);
561 if (!calendar) goto after_banner;
562 CFTimeZoneRef tz = CFTimeZoneCopySystem();
563 if (!tz) {
564 CFRelease(calendar);
565 goto after_banner;
566 }
567 CFCalendarSetTimeZone(calendar, tz);
568 CFRelease(tz);
569 int32_t year, month, day, hour, minute, second;
570 Boolean dec = CFCalendarDecomposeAbsoluteTime(calendar, at, "yMdHms", &year, &month, &day, &hour, &minute, &second);
571 CFRelease(calendar);
572 if (!dec) goto after_banner;
573 double atf;
574 int32_t ms = (int32_t)floor(1000.0 * modf(at, &atf));
575 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
576 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()));
577 asprintf(&thread, "%x", pthread_mach_thread_np(pthread_self()));
578 #elif DEPLOYMENT_TARGET_WINDOWS
579 bannerLen = asprintf(&banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%x] ", year, month, day, hour, minute, second, ms, *_CFGetProgname(), getpid(), GetCurrentThreadId());
580 asprintf(&thread, "%x", GetCurrentThreadId());
581 #else
582 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());
583 asprintf(&thread, "%x", pthread_self());
584 #endif
585 asprintf(&time, "%04d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, ms);
586
587 }
588 after_banner:;
589 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
590 asprintf(&uid, "%d", geteuid());
591 aslclient asl = asl_open(NULL, __CFBundleMainID[0] ? __CFBundleMainID : "com.apple.console", ASL_OPT_NO_DELAY);
592 aslmsg msg = asl_new(ASL_TYPE_MSG);
593 asl_set(msg, "CFLog Local Time", time); // not to be documented, not public API
594 asl_set(msg, "CFLog Thread", thread); // not to be documented, not public API
595 asl_set(msg, "ReadUID", uid);
596 static const char *levstr[] = {"0", "1", "2", "3", "4", "5", "6", "7"};
597 asl_set(msg, ASL_KEY_LEVEL, levstr[lev]);
598 asl_set(msg, ASL_KEY_MSG, message);
599 asl_send(asl, msg);
600 asl_free(msg);
601 asl_close(asl);
602 #endif
603
604 if (also_do_stderr()) {
605 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
606 struct iovec v[3];
607 v[0].iov_base = banner;
608 v[0].iov_len = banner ? strlen(banner) : 0;
609 v[1].iov_base = (char *)message;
610 v[1].iov_len = length;
611 v[2].iov_base = "\n";
612 v[2].iov_len = (message[length - 1] != '\n') ? 1 : 0;
613 int nv = (v[0].iov_base ? 1 : 0) + 1 + (v[2].iov_len ? 1 : 0);
614 static CFSpinLock_t lock = CFSpinLockInit;
615 __CFSpinLock(&lock);
616 writev(STDERR_FILENO, v[0].iov_base ? v : v + 1, nv);
617 __CFSpinUnlock(&lock);
618 #elif DEPLOYMENT_TARGET_WINDOWS
619 size_t bufLen = bannerLen + length + 1;
620 char *buf = (char *)malloc(sizeof(char) * bufLen);
621 if (banner) {
622 // Copy the banner into the debug string
623 memmove_s(buf, bufLen, banner, bannerLen);
624
625 // Copy the message into the debug string
626 strcpy_s(buf + bannerLen, bufLen - bannerLen, message);
627 } else {
628 strcpy_s(buf, bufLen, message);
629 }
630 buf[bufLen - 1] = '\0';
631 fprintf_s(stderr, "%s\n", buf);
632 // This Win32 API call only prints when a debugger is active
633 // OutputDebugStringA(buf);
634 free(buf);
635 #else
636 size_t bufLen = bannerLen + length + 1;
637 char *buf = (char *)malloc(sizeof(char) * bufLen);
638 if (banner) {
639 // Copy the banner into the debug string
640 memmove(buf, banner, bannerLen);
641
642 // Copy the message into the debug string
643 strncpy(buf + bannerLen, message, bufLen - bannerLen);
644 } else {
645 strncpy(buf, message, bufLen);
646 }
647 buf[bufLen - 1] = '\0';
648 fprintf(stderr, "%s\n", buf);
649 free(buf);
650 #endif
651 }
652
653 if (thread) free(thread);
654 if (time) free(time);
655 if (banner) free(banner);
656 if (uid) free(uid);
657 }
658
659 CF_EXPORT void _CFLogvEx(CFLogFunc logit, CFStringRef (*copyDescFunc)(void *, const void *), CFDictionaryRef formatOptions, int32_t lev, CFStringRef format, va_list args) {
660 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
661 uintptr_t val = (uintptr_t)_CFGetTSD(__CFTSDKeyIsInCFLog);
662 if (3 < val) return; // allow up to 4 nested invocations
663 _CFSetTSD(__CFTSDKeyIsInCFLog, (void *)(val + 1), NULL);
664 #endif
665 CFStringRef str = format ? _CFStringCreateWithFormatAndArgumentsAux(kCFAllocatorSystemDefault, copyDescFunc, formatOptions, (CFStringRef)format, args) : 0;
666 CFIndex blen = str ? CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1 : 0;
667 char *buf = str ? (char *)malloc(blen) : 0;
668 if (str && buf) {
669 Boolean converted = CFStringGetCString(str, buf, blen, kCFStringEncodingUTF8);
670 size_t len = strlen(buf);
671 // silently ignore 0-length or really large messages, and levels outside the valid range
672 if (converted && !(len <= 0 || (1 << 24) < len) && !(lev < ASL_LEVEL_EMERG || ASL_LEVEL_DEBUG < lev)) {
673 (logit ? logit : __CFLogCString)(lev, buf, len, 1);
674 }
675 }
676 if (buf) free(buf);
677 if (str) CFRelease(str);
678 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
679 _CFSetTSD(__CFTSDKeyIsInCFLog, (void *)val, NULL);
680 #endif
681 }
682
683 // 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
684 __private_extern__ void _CFLogSimple(int32_t lev, char *format, ...) {
685 va_list args;
686 va_start(args, format);
687 char formattedMessage[1024];
688 int length = vsnprintf(formattedMessage, 1024, format, args);
689 if (length > 0) {
690 __CFLogCString(lev, formattedMessage, length, 0);
691 }
692 va_end(args);
693 }
694
695 void CFLog(int32_t lev, CFStringRef format, ...) {
696 va_list args;
697 va_start(args, format);
698 _CFLogvEx(NULL, NULL, NULL, lev, format, args);
699 va_end(args);
700 }
701
702
703
704 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
705
706 kern_return_t _CFDiscorporateMemoryAllocate(CFDiscorporateMemory *hm, size_t size, bool purgeable) {
707 kern_return_t ret = KERN_SUCCESS;
708 size = round_page(size);
709 if (0 == size) size = vm_page_size;
710 memset(hm, 0, sizeof(CFDiscorporateMemory));
711 void *addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, VM_MAKE_TAG(0) | (purgeable ? VM_FLAGS_PURGABLE : 0), 0);
712 if ((uintptr_t)addr == -1) {
713 ret = KERN_NO_SPACE;
714 }
715 if (KERN_SUCCESS == ret) {
716 hm->address = (mach_vm_address_t)(uintptr_t)addr;
717 hm->size = (mach_vm_size_t)size;
718 hm->port = MACH_PORT_NULL;
719 hm->corporeal = true;
720 hm->purgeable = purgeable;
721 }
722 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);
723 if (KERN_SUCCESS == ret) hm->corporeal = true;
724 return ret;
725 }
726
727 kern_return_t _CFDiscorporateMemoryDeallocate(CFDiscorporateMemory *hm) {
728 kern_return_t ret1 = KERN_SUCCESS, ret2 = KERN_SUCCESS;
729 if (hm->corporeal) ret1 = mach_vm_deallocate(mach_task_self(), hm->address, hm->size);
730 hm->address = MACH_VM_MIN_ADDRESS;
731 hm->corporeal = false;
732 ret2 = mach_port_deallocate(mach_task_self(), hm->port);
733 hm->port = MACH_PORT_NULL;
734 return ret1 != KERN_SUCCESS ? ret1 : ret2;
735 }
736
737 kern_return_t _CFDiscorporateMemoryDematerialize(CFDiscorporateMemory *hm) {
738 kern_return_t ret = KERN_SUCCESS;
739 if (!hm->corporeal) ret = KERN_INVALID_MEMORY_CONTROL;
740 int state = VM_PURGABLE_VOLATILE;
741 if (KERN_SUCCESS == ret) vm_purgable_control(mach_task_self(), (vm_address_t)hm->address, VM_PURGABLE_SET_STATE, &state);
742 if (KERN_SUCCESS == ret) ret = mach_vm_deallocate(mach_task_self(), hm->address, hm->size);
743 if (KERN_SUCCESS == ret) hm->address = MACH_VM_MIN_ADDRESS;
744 if (KERN_SUCCESS == ret) hm->corporeal = false;
745 return ret;
746 }
747
748 kern_return_t _CFDiscorporateMemoryMaterialize(CFDiscorporateMemory *hm) {
749 kern_return_t ret = KERN_SUCCESS;
750 if (hm->corporeal) ret = KERN_INVALID_MEMORY_CONTROL;
751 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);
752 if (KERN_SUCCESS == ret) hm->corporeal = true;
753 int state = VM_PURGABLE_NONVOLATILE;
754 if (KERN_SUCCESS == ret) ret = vm_purgable_control(mach_task_self(), (vm_address_t)hm->address, VM_PURGABLE_SET_STATE, &state);
755 if (KERN_SUCCESS == ret) if (VM_PURGABLE_EMPTY == state) ret = KERN_PROTECTION_FAILURE; // same as VM_PURGABLE_EMPTY
756 return ret;
757 }
758
759 #endif
760
761 #if DEPLOYMENT_TARGET_MACOSX
762
763 #define SUDDEN_TERMINATION_ENABLE_VPROC 1
764
765 #if SUDDEN_TERMINATION_ENABLE_VPROC
766
767 static CFSpinLock_t __CFProcessKillingLock = CFSpinLockInit;
768 static CFIndex __CFProcessKillingDisablingCount = 1;
769 static Boolean __CFProcessKillingWasTurnedOn = false;
770
771 void _CFSuddenTerminationDisable(void) {
772 __CFSpinLock(&__CFProcessKillingLock);
773 __CFProcessKillingDisablingCount++;
774 _vproc_transaction_begin();
775 __CFSpinUnlock(&__CFProcessKillingLock);
776 }
777
778 void _CFSuddenTerminationEnable(void) {
779 // 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.
780 __CFSpinLock(&__CFProcessKillingLock);
781 __CFProcessKillingDisablingCount--;
782 if (__CFProcessKillingDisablingCount==0 && !__CFProcessKillingWasTurnedOn) {
783 int64_t transactionsAreToBeEnabled = 1;
784 int64_t transactionsWereAlreadyEnabled = 0;
785 vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_TRANSACTIONS_ENABLED, &transactionsAreToBeEnabled, &transactionsWereAlreadyEnabled);
786 if (!verr) {
787 if (!transactionsWereAlreadyEnabled) {
788 // We set __CFProcessKillingWasTurnedOn below regardless of success because there's no point in retrying.
789 } // else this process was launched by launchd with transactions already enabled because EnableTransactions was set to true in the launchd .plist file.
790 } // else this process was not launched by launchd and the fix for 6416724 is not in the build yet.
791 __CFProcessKillingWasTurnedOn = true;
792 } else {
793 // 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.
794 if (__CFProcessKillingDisablingCount>=0) {
795 _vproc_transaction_end();
796 } else {
797 CFLog(kCFLogLevelError, CFSTR("-[NSProcessInfo enableSuddenTermination] has been invoked more times than necessary to balance invocations of -[NSProcessInfo disableSuddenTermination]. Ignoring."));
798 }
799 }
800 __CFSpinUnlock(&__CFProcessKillingLock);
801 }
802
803 void _CFSuddenTerminationExitIfTerminationEnabled(int exitStatus) {
804 // 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.
805 __CFSpinLock(&__CFProcessKillingLock);
806 // 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.
807 if (_vproc_transaction_count()==0 && __CFProcessKillingWasTurnedOn) {
808 _exit(exitStatus);
809 }
810 __CFSpinUnlock(&__CFProcessKillingLock);
811 }
812
813 void _CFSuddenTerminationExitWhenTerminationEnabled(int exitStatus) {
814 // The user has had their final opportunity to cancel quitting. Exit as soon as the process is clean. Same carefulness as in _CFSuddenTerminationExitIfTerminationEnabled().
815 __CFSpinLock(&__CFProcessKillingLock);
816 if (__CFProcessKillingWasTurnedOn) {
817 _vproc_transaction_try_exit(exitStatus);
818 }
819 __CFSpinUnlock(&__CFProcessKillingLock);
820 }
821
822 size_t _CFSuddenTerminationDisablingCount(void) {
823 // 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.
824 return _vproc_transaction_count() + (__CFProcessKillingWasTurnedOn ? 0 : 1);
825 }
826
827 #else
828
829 #warning Building with vproc sudden termination API disabled.
830
831 static CFSpinLock_t __CFProcessKillingLock = CFSpinLockInit;
832 static size_t __CFProcessKillingDisablingCount = 1;
833 static Boolean __CFProcessExitNextTimeKillingIsEnabled = false;
834 static int32_t __CFProcessExitStatus = 0;
835 static int __CFProcessIsKillableNotifyToken;
836 static Boolean __CFProcessIsKillableNotifyTokenIsFigured = false;
837
838 __private_extern__ void _CFSetSuddenTerminationEnabled(Boolean isEnabled) {
839 if (!__CFProcessIsKillableNotifyTokenIsFigured) {
840 char *notificationName = NULL;
841 asprintf(&notificationName, "com.apple.isKillable.%i", getpid());
842 uint32_t notifyResult = notify_register_check(notificationName, &__CFProcessIsKillableNotifyToken);
843 if (notifyResult != NOTIFY_STATUS_OK) {
844 CFLog(kCFLogLevelError, CFSTR("%s: notify_register_check() returned %i."), __PRETTY_FUNCTION__, notifyResult);
845 }
846 free(notificationName);
847 __CFProcessIsKillableNotifyTokenIsFigured = true;
848 }
849 uint32_t notifyResult = notify_set_state(__CFProcessIsKillableNotifyToken, isEnabled);
850 if (notifyResult != NOTIFY_STATUS_OK) {
851 CFLog(kCFLogLevelError, CFSTR("%s: notify_set_state() returned %i"), __PRETTY_FUNCTION__, notifyResult);
852 }
853 }
854
855 void _CFSuddenTerminationDisable(void) {
856 __CFSpinLock(&__CFProcessKillingLock);
857 if (__CFProcessKillingDisablingCount == 0) {
858 _CFSetSuddenTerminationEnabled(false);
859 }
860 __CFProcessKillingDisablingCount++;
861 __CFSpinUnlock(&__CFProcessKillingLock);
862 }
863
864 void _CFSuddenTerminationEnable(void) {
865 __CFSpinLock(&__CFProcessKillingLock);
866 __CFProcessKillingDisablingCount--;
867 if (__CFProcessKillingDisablingCount == 0) {
868 if (__CFProcessExitNextTimeKillingIsEnabled) {
869 _exit(__CFProcessExitStatus);
870 } else {
871 _CFSetSuddenTerminationEnabled(true);
872 }
873 }
874 __CFSpinUnlock(&__CFProcessKillingLock);
875 }
876
877 void _CFSuddenTerminationExitIfTerminationEnabled(int exitStatus) {
878 __CFSpinLock(&__CFProcessKillingLock);
879 if (__CFProcessKillingDisablingCount == 0) {
880 _exit(exitStatus);
881 }
882 __CFSpinUnlock(&__CFProcessKillingLock);
883 }
884
885 void _CFSuddenTerminationExitWhenTerminationEnabled(int exitStatus) {
886 __CFSpinLock(&__CFProcessKillingLock);
887 if (__CFProcessKillingDisablingCount == 0) {
888 _exit(exitStatus);
889 } else {
890 __CFProcessExitNextTimeKillingIsEnabled = YES;
891 __CFProcessExitStatus = exitStatus;
892 }
893 __CFSpinUnlock(&__CFProcessKillingLock);
894 }
895
896 size_t _CFSuddenTerminationDisablingCount(void) {
897 return __CFProcessKillingDisablingCount;
898 }
899
900 #endif
901
902 #endif
903
904 #if 0
905 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
906
907 typedef void (^ThrottleTypeA)(void); // allows calls per nanoseconds
908 typedef void (^ThrottleTypeB)(uint64_t amt); // allows amount per nanoseconds
909
910 __private_extern__ ThrottleTypeA __CFCreateThrottleTypeA(uint16_t calls, uint64_t nanoseconds) {
911 struct mach_timebase_info info;
912 mach_timebase_info(&info);
913 uint64_t period = nanoseconds / info.numer * info.denom;
914
915 if (0 == calls || 0 == period) return NULL;
916
917 __block OSSpinLock b_lock = OS_SPINLOCK_INIT;
918 __block uint64_t b_values[calls];
919 __block uint64_t *b_oldest = b_values;
920 memset(b_values, 0, sizeof(b_values));
921
922 return Block_copy(^{
923 uint64_t curr_time = mach_absolute_time();
924 OSSpinLockLock(&b_lock);
925 uint64_t next_time = *b_oldest + period;
926 *b_oldest = (curr_time < next_time) ? next_time : curr_time;
927 b_oldest++;
928 if (b_values + calls <= b_oldest) b_oldest = b_values;
929 OSSpinLockUnlock(&b_lock);
930 if (curr_time < next_time) {
931 mach_wait_until(next_time);
932 }
933 });
934 }
935
936 __private_extern__ ThrottleTypeB __CFCreateThrottleTypeB(uint64_t amount, uint64_t nanoseconds) {
937 struct mach_timebase_info info;
938 mach_timebase_info(&info);
939 uint64_t period = nanoseconds / info.numer * info.denom;
940
941 if (0 == amount || 0 == period) return NULL;
942
943 __block OSSpinLock b_lock = OS_SPINLOCK_INIT;
944 __block uint64_t b_sum = 0ULL;
945 __block uint16_t b_num_values = 8;
946 __block uint64_t *b_values = calloc(b_num_values, 2 * sizeof(uint64_t));
947 __block uint64_t *b_oldest = b_values;
948
949 return Block_copy(^(uint64_t amt){
950 OSSpinLockLock(&b_lock);
951 // unimplemented
952 OSSpinLockUnlock(&b_lock);
953 });
954 }
955
956 #endif
957 #endif
958