1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 34 #include "Platform.h" 35 36 #ifdef MAC 37 38 #include "MacPlatform.h" 39 #include "Helpers.h" 40 #include "Package.h" 41 #include "PropertyFile.h" 42 #include "IniFile.h" 43 44 #include <sys/sysctl.h> 45 #include <pthread.h> 46 #include <vector> 47 48 #import <Foundation/Foundation.h> 49 50 #include <CoreFoundation/CoreFoundation.h> 51 #include <CoreFoundation/CFString.h> 52 53 #ifdef __OBJC__ 54 #import <Cocoa/Cocoa.h> 55 #endif //__OBJC__ 56 57 //-------------------------------------------------------------------------------------------------- 58 59 NSString* StringToNSString(TString Value) { 60 NSString* result = [NSString stringWithCString:Value.c_str() 61 encoding:[NSString defaultCStringEncoding]]; 62 return result; 63 } 64 65 //-------------------------------------------------------------------------------------------------- 66 67 MacPlatform::MacPlatform(void) : Platform(), GenericPlatform(), PosixPlatform() { 68 } 69 70 MacPlatform::~MacPlatform(void) { 71 } 72 73 bool MacPlatform::UsePListForConfigFile() { 74 return FilePath::FileExists(GetConfigFileName()) == false; 75 } 76 77 void MacPlatform::ShowMessage(TString Title, TString Description) { 78 NSString *ltitle = StringToNSString(Title); 79 NSString *ldescription = StringToNSString(Description); 80 81 NSLog(@"%@:%@", ltitle, ldescription); 82 } 83 84 void MacPlatform::ShowMessage(TString Description) { 85 TString appname = GetModuleFileName(); 86 appname = FilePath::ExtractFileName(appname); 87 ShowMessage(appname, Description); 88 } 89 90 91 TCHAR* MacPlatform::ConvertStringToFileSystemString(TCHAR* Source, bool &release) { 92 TCHAR* result = NULL; 93 release = false; 94 CFStringRef StringRef = CFStringCreateWithCString(kCFAllocatorDefault, Source, kCFStringEncodingUTF8); 95 96 if (StringRef != NULL) { 97 @try { 98 CFIndex length = CFStringGetMaximumSizeOfFileSystemRepresentation(StringRef); 99 result = new char[length + 1]; 100 101 if (CFStringGetFileSystemRepresentation(StringRef, result, length)) { 102 release = true; 103 } 104 else { 105 delete[] result; 106 result = NULL; 107 } 108 } 109 @finally { 110 CFRelease(StringRef); 111 } 112 } 113 114 return result; 115 } 116 117 TCHAR* MacPlatform::ConvertFileSystemStringToString(TCHAR* Source, bool &release) { 118 TCHAR* result = NULL; 119 release = false; 120 CFStringRef StringRef = CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, Source); 121 122 if (StringRef != NULL) { 123 @try { 124 CFIndex length = CFStringGetLength(StringRef); 125 126 if (length > 0) { 127 CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); 128 129 result = new char[maxSize + 1]; 130 131 if (CFStringGetCString(StringRef, result, maxSize, kCFStringEncodingUTF8) == true) { 132 release = true; 133 } 134 else { 135 delete[] result; 136 result = NULL; 137 } 138 } 139 } 140 @finally { 141 CFRelease(StringRef); 142 } 143 } 144 145 return result; 146 } 147 148 void MacPlatform::SetCurrentDirectory(TString Value) { 149 chdir(PlatformString(Value).toPlatformString()); 150 } 151 152 TString MacPlatform::GetPackageRootDirectory() { 153 NSBundle *mainBundle = [NSBundle mainBundle]; 154 NSString *mainBundlePath = [mainBundle bundlePath]; 155 NSString *contentsPath = [mainBundlePath stringByAppendingString:@"/Contents"]; 156 TString result = [contentsPath UTF8String]; 157 return result; 158 } 159 160 TString MacPlatform::GetAppDataDirectory() { 161 TString result; 162 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); 163 NSString *applicationSupportDirectory = [paths firstObject]; 164 result = [applicationSupportDirectory UTF8String]; 165 return result; 166 } 167 168 TString MacPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) { 169 TString result; 170 171 result = FilePath::IncludeTrailingSeparater(RuntimePath) + _T("Contents/Home/jre/lib/jli/libjli.dylib"); 172 173 if (FilePath::FileExists(result) == false) { 174 result = FilePath::IncludeTrailingSeparater(RuntimePath) + _T("Contents/Home/lib/jli/libjli.dylib"); 175 176 if (FilePath::FileExists(result) == false) { 177 result = _T(""); 178 } 179 } 180 181 return result; 182 } 183 184 185 TString MacPlatform::GetSystemJRE() { 186 if (GetAppCDSState() == cdsOn || GetAppCDSState() == cdsGenCache) { 187 //TODO throw exception 188 return _T(""); 189 } 190 191 return _T("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/jli/libjli.dylib"); 192 } 193 194 TString MacPlatform::GetSystemJVMLibraryFileName() { 195 TString result = GetSystemJRE(); 196 197 if (FilePath::FileExists(result) == false) { 198 result = _T(""); 199 } 200 201 return result; 202 } 203 204 TString MacPlatform::GetAppName() { 205 NSString *appName = [[NSProcessInfo processInfo] processName]; 206 TString result = [appName UTF8String]; 207 return result; 208 } 209 210 void AppendPListArrayToIniFile(NSDictionary *infoDictionary, IniFile *result, TString Section) { 211 NSString *sectionKey = [NSString stringWithUTF8String:PlatformString(Section).toMultibyte()]; 212 NSDictionary *array = [infoDictionary objectForKey:sectionKey]; 213 214 for (id option in array) { 215 if ([option isKindOfClass:[NSString class]]) { 216 TString arg = [option UTF8String]; 217 218 TString name; 219 TString value; 220 221 if (Helpers::SplitOptionIntoNameValue(arg, name, value) == true) { 222 result->Append(Section, name, value); 223 } 224 } 225 } 226 } 227 228 void AppendPListDictionaryToIniFile(NSDictionary *infoDictionary, IniFile *result, TString Section, bool FollowSection = true) { 229 NSDictionary *dictionary = NULL; 230 231 if (FollowSection == true) { 232 NSString *sectionKey = [NSString stringWithUTF8String:PlatformString(Section).toMultibyte()]; 233 dictionary = [infoDictionary objectForKey:sectionKey]; 234 } 235 else { 236 dictionary = infoDictionary; 237 } 238 239 for (id key in dictionary) { 240 id option = [dictionary valueForKey:key]; 241 242 if ([key isKindOfClass:[NSString class]] && [option isKindOfClass:[NSString class]]) { 243 TString name = [key UTF8String]; 244 TString value = [option UTF8String]; 245 result->Append(Section, name, value); 246 } 247 } 248 } 249 250 // Convert parts of the info.plist to the INI format the rest of the packager uses unless 251 // a packager config file exists. 252 ISectionalPropertyContainer* MacPlatform::GetConfigFile(TString FileName) { 253 IniFile* result = new IniFile(); 254 255 if (UsePListForConfigFile() == false) { 256 if (result->LoadFromFile(FileName) == false) { 257 // New property file format was not found, attempt to load old property file format. 258 Helpers::LoadOldConfigFile(FileName, result); 259 } 260 } 261 else { 262 NSBundle *mainBundle = [NSBundle mainBundle]; 263 NSDictionary *infoDictionary = [mainBundle infoDictionary]; 264 std::map<TString, TString> keys = GetKeys(); 265 266 // Packager options. 267 AppendPListDictionaryToIniFile(infoDictionary, result, keys[CONFIG_SECTION_APPLICATION], false); 268 269 // jvmargs 270 AppendPListArrayToIniFile(infoDictionary, result, keys[CONFIG_SECTION_JVMOPTIONS]); 271 272 // jvmuserargs 273 AppendPListDictionaryToIniFile(infoDictionary, result, keys[CONFIG_SECTION_JVMUSEROPTIONS]); 274 275 // Generate AppCDS Cache 276 AppendPListDictionaryToIniFile(infoDictionary, result, keys[CONFIG_SECTION_APPCDSJVMOPTIONS]); 277 AppendPListDictionaryToIniFile(infoDictionary, result, keys[CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS]); 278 279 // args 280 AppendPListArrayToIniFile(infoDictionary, result, keys[CONFIG_SECTION_ARGOPTIONS]); 281 } 282 283 return result; 284 } 285 /* 286 #include <mach-o/dyld.h> 287 #include <mach-o/getsect.h> 288 #include <dlfcn.h> 289 #include <string> 290 291 std::string GetModuleFileName(const void *module) 292 { 293 if (!module) { return std::string(); } 294 uint32_t count = _dyld_image_count(); 295 for (uint32_t i = 0; i < count; ++i) { 296 const mach_header *header = _dyld_get_image_header(i); 297 if (!header) { break; } 298 char *code_ptr = NULL; 299 if ((header->magic & MH_MAGIC_64) == MH_MAGIC_64) { 300 uint64_t size; 301 code_ptr = getsectdatafromheader_64((const mach_header_64 *)header, SEG_TEXT, SECT_TEXT, &size); 302 } else { 303 uint32_t size; 304 code_ptr = getsectdatafromheader(header, SEG_TEXT, SECT_TEXT, &size); 305 } 306 if (!code_ptr) { continue; } 307 const uintptr_t slide = _dyld_get_image_vmaddr_slide(i); 308 const uintptr_t start = (const uintptr_t)code_ptr + slide; 309 Dl_info info; 310 if (dladdr((const void *)start, &info)) { 311 if (dlopen(info.dli_fname, RTLD_NOW) == module) { 312 return std::string(info.dli_fname); 313 } 314 } 315 } 316 return std::string(); 317 }*/ 318 319 TString GetModuleFileNameOSX() { 320 Dl_info module_info; 321 if (dladdr(reinterpret_cast<void*>(GetModuleFileNameOSX), &module_info) == 0) { 322 // Failed to find the symbol we asked for. 323 return std::string(); 324 } 325 return TString(module_info.dli_fname); 326 } 327 328 #include <mach-o/dyld.h> 329 330 TString MacPlatform::GetModuleFileName() { 331 //return GetModuleFileNameOSX(); 332 333 TString result; 334 DynamicBuffer<TCHAR> buffer(MAX_PATH); 335 uint32_t size = buffer.GetSize(); 336 337 if (_NSGetExecutablePath(buffer.GetData(), &size) == 0) { 338 result = FileSystemStringToString(buffer.GetData()); 339 } 340 341 return result; 342 } 343 344 bool MacPlatform::IsMainThread() { 345 bool result = (pthread_main_np() == 1); 346 return result; 347 } 348 349 TPlatformNumber MacPlatform::GetMemorySize() { 350 unsigned long long memory = [[NSProcessInfo processInfo] physicalMemory]; 351 TPlatformNumber result = memory / 1048576; // Convert from bytes to megabytes. 352 return result; 353 } 354 355 std::map<TString, TString> MacPlatform::GetKeys() { 356 std::map<TString, TString> keys; 357 358 if (UsePListForConfigFile() == false) { 359 return GenericPlatform::GetKeys(); 360 } 361 else { 362 keys.insert(std::map<TString, TString>::value_type(CONFIG_VERSION, _T("app.version"))); 363 keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINJAR_KEY, _T("JVMMainJarName"))); 364 keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINCLASSNAME_KEY, _T("JVMMainClassName"))); 365 keys.insert(std::map<TString, TString>::value_type(CONFIG_CLASSPATH_KEY, _T("JVMAppClasspath"))); 366 keys.insert(std::map<TString, TString>::value_type(APP_NAME_KEY, _T("CFBundleName"))); 367 keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_ID_KEY, _T("JVMPreferencesID"))); 368 keys.insert(std::map<TString, TString>::value_type(JVM_RUNTIME_KEY, _T("JVMRuntime"))); 369 keys.insert(std::map<TString, TString>::value_type(PACKAGER_APP_DATA_DIR, _T("CFBundleIdentifier"))); 370 371 keys.insert(std::map<TString, TString>::value_type(CONFIG_SPLASH_KEY, _T("app.splash"))); 372 keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_MEMORY, _T("app.memory"))); 373 374 keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_APPLICATION, _T("Application"))); 375 keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_JVMOPTIONS, _T("JVMOptions"))); 376 keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_JVMUSEROPTIONS, _T("JVMUserOptions"))); 377 keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_JVMUSEROVERRIDESOPTIONS, _T("JVMUserOverrideOptions"))); 378 keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_APPCDSJVMOPTIONS, _T("AppCDSJVMOptions"))); 379 keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS, _T("AppCDSGenerateCacheJVMOptions"))); 380 keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_ARGOPTIONS, _T("ArgOptions"))); 381 } 382 383 return keys; 384 } 385 386 #ifdef DEBUG 387 bool MacPlatform::IsNativeDebuggerPresent() { 388 int state; 389 int mib[4]; 390 struct kinfo_proc info; 391 size_t size; 392 393 info.kp_proc.p_flag = 0; 394 395 mib[0] = CTL_KERN; 396 mib[1] = KERN_PROC; 397 mib[2] = KERN_PROC_PID; 398 mib[3] = getpid(); 399 400 size = sizeof(info); 401 state = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); 402 assert(state == 0); 403 return ((info.kp_proc.p_flag & P_TRACED) != 0); 404 } 405 406 int MacPlatform::GetProcessID() { 407 int pid = [[NSProcessInfo processInfo] processIdentifier]; 408 return pid; 409 } 410 #endif //DEBUG 411 412 //-------------------------------------------------------------------------------------------------- 413 414 class UserDefaults { 415 private: 416 OrderedMap<TString, TString> FData; 417 TString FDomainName; 418 419 bool ReadDictionary(NSDictionary *Items, OrderedMap<TString, TString> &Data) { 420 bool result = false; 421 422 for (id key in Items) { 423 id option = [Items valueForKey:key]; 424 425 if ([key isKindOfClass:[NSString class]] && [option isKindOfClass:[NSString class]]) { 426 TString name = [key UTF8String]; 427 TString value = [option UTF8String]; 428 429 if (name.empty() == false) { 430 Data.Append(name, value); 431 } 432 } 433 } 434 435 return result; 436 } 437 438 // Open and read the defaults file specified by domain. 439 bool ReadPreferences(NSDictionary *Defaults, std::list<TString> Keys, OrderedMap<TString, TString> &Data) { 440 bool result = false; 441 442 if (Keys.size() > 0 && Defaults != NULL) { 443 NSDictionary *node = Defaults; 444 445 while (Keys.size() > 0 && node != NULL) { 446 TString key = Keys.front(); 447 Keys.pop_front(); 448 NSString *tempKey = StringToNSString(key); 449 node = [node valueForKey:tempKey]; 450 451 if (Keys.size() == 0) { 452 break; 453 } 454 } 455 456 if (node != NULL) { 457 result = ReadDictionary(node, Data); 458 } 459 } 460 461 return result; 462 } 463 464 NSDictionary* LoadPreferences(TString DomainName) { 465 NSDictionary *result = NULL; 466 467 if (DomainName.empty() == false) { 468 NSUserDefaults *prefs = [[NSUserDefaults alloc] init]; 469 470 if (prefs != NULL) { 471 NSString *lDomainName = StringToNSString(DomainName); 472 result = [prefs persistentDomainForName: lDomainName]; 473 } 474 } 475 476 return result; 477 } 478 479 public: 480 UserDefaults(TString DomainName) { 481 FDomainName = DomainName; 482 } 483 484 bool Read(std::list<TString> Keys) { 485 NSDictionary *defaults = LoadPreferences(FDomainName); 486 return ReadPreferences(defaults, Keys, FData); 487 } 488 489 OrderedMap<TString, TString> GetData() { 490 return FData; 491 } 492 }; 493 494 //-------------------------------------------------------------------------------------------------- 495 496 MacJavaUserPreferences::MacJavaUserPreferences(void) : JavaUserPreferences() { 497 } 498 499 TString toLowerCase(TString Value) { 500 // Use Cocoa's lowercase method because it is better than the ones provided by C/C++. 501 NSString *temp = StringToNSString(Value); 502 temp = [temp lowercaseString]; 503 TString result = [temp UTF8String]; 504 return result; 505 } 506 507 // Split the string Value into using Delimiter. 508 std::list<TString> Split(TString Value, TString Delimiter) { 509 std::list<TString> result; 510 std::vector<char> buffer(Value.c_str(), Value.c_str() + Value.size() + 1); 511 char *p = strtok(&buffer[0], Delimiter.data()); 512 513 while (p != NULL) { 514 TString token = p; 515 result.push_back(token); 516 p = strtok(NULL, Delimiter.data()); 517 } 518 519 return result; 520 } 521 522 // 1. If the path is fewer than three components (Example: one/two/three) then the domain is the 523 // default domain "com.apple.java.util.prefs" stored in the plist file 524 // ~/Library/Preferences/com.apple.java.util.prefs.plist 525 // 526 // For example: If AppID = "hello", the path is "hello/JVMUserOptions and the 527 // plist file is ~/Library/Preferences/com.apple.java.util.prefs.plist containing the contents: 528 // 529 // <?xml version="1.0" encoding="UTF-8"?> 530 // <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 531 // <plist version="1.0"> 532 // <dict> 533 // <key>/</key> 534 // <dict> 535 // <key>hello/</key> 536 // <dict> 537 // <key>JVMUserOptions/</key> 538 // <dict> 539 // <key>-DXmx</key> 540 // <string>512m</string> 541 // </dict> 542 // </dict> 543 // </dict> 544 // </dict> 545 // </plist> 546 // 547 // 2. If the path is three or more, the first three become the domain name (even 548 // if shared across applicaitons) and the remaining become individual keys. 549 // 550 // For example: If AppID = "com/hello/foo", the path is "hello/JVMUserOptions and the 551 // domain is "com.hello.foo" stored in the plist file ~/Library/Preferences/com.hello.foo.plist 552 // containing the contents: 553 // 554 // <?xml version="1.0" encoding="UTF-8"?> 555 // <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 556 // <plist version="1.0"> 557 // <dict> 558 // <key>/com/hello/foo/</key> 559 // <dict> 560 // <key>JVMUserOptions/</key> 561 // <dict> 562 // <key>-DXmx</key> 563 // <string>512m</string> 564 // </dict> 565 // </dict> 566 // </dict> 567 // </plist> 568 // 569 // NOTE: To change these values use the command line utility "defaults": 570 // Example: defaults read com.apple.java.util.prefs / 571 // Since OS 10.9 Mavericks the defaults are cashed so directly modifying the files is not recommended. 572 bool MacJavaUserPreferences::Load(TString Appid) { 573 bool result = false; 574 575 if (Appid.empty() == false) { 576 // This is for backwards compatability. Older packaged applications have an 577 // app.preferences.id that is delimited by period (".") rather than 578 // slash ("/") so convert to newer style. 579 TString path = Helpers::ReplaceString(Appid, _T("."), _T("/")); 580 581 path = path + _T("/JVMUserOptions"); 582 TString domainName; 583 std::list<TString> keys = Split(path, _T("/")); 584 585 // If there are less than three parts to the path then use the default preferences file. 586 if (keys.size() < 3) { 587 domainName = _T("com.apple.java.util.prefs"); 588 589 // Append slash to the end of each key. 590 for (std::list<TString>::iterator iterator = keys.begin(); iterator != keys.end(); iterator++) { 591 TString item = *iterator; 592 item = item + _T("/"); 593 *iterator = item; 594 } 595 596 // The root key is /. 597 keys.push_front(_T("/")); 598 } 599 else { 600 // Remove the first three keys and use them for the root key and the preferencesID. 601 TString one = keys.front(); 602 keys.pop_front(); 603 TString two = keys.front(); 604 keys.pop_front(); 605 TString three = keys.front(); 606 keys.pop_front(); 607 domainName = one + TString(".") + two + TString(".") + three; 608 domainName = toLowerCase(domainName); 609 610 // Append slash to the end of each key. 611 for (std::list<TString>::iterator iterator = keys.begin(); iterator != keys.end(); iterator++) { 612 TString item = *iterator; 613 item = item + _T("/"); 614 *iterator = item; 615 } 616 617 // The root key is /one/two/three/ 618 TString key = TString("/") + one + TString("/") + two + TString("/") + three + TString("/"); 619 keys.push_front(key); 620 } 621 622 UserDefaults userDefaults(domainName); 623 624 if (userDefaults.Read(keys) == true) { 625 result = true; 626 FMap = userDefaults.GetData(); 627 } 628 } 629 630 return result; 631 } 632 633 //-------------------------------------------------------------------------------------------------- 634 635 #endif //MAC