1 /* 2 * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "Platform.h" 27 28 #include "MacPlatform.h" 29 #include "Helpers.h" 30 #include "Package.h" 31 #include "PropertyFile.h" 32 #include "IniFile.h" 33 34 #include <sys/sysctl.h> 35 #include <pthread.h> 36 #include <vector> 37 #include <signal.h> 38 #include <mach-o/dyld.h> 39 40 #import <Foundation/Foundation.h> 41 #import <AppKit/NSRunningApplication.h> 42 43 #include <CoreFoundation/CoreFoundation.h> 44 #include <CoreFoundation/CFString.h> 45 46 #ifdef __OBJC__ 47 #import <Cocoa/Cocoa.h> 48 #endif //__OBJC__ 49 50 #define MAC_JPACKAGE_TMP_DIR \ 51 "/Library/Application Support/Java/JPackage/tmp" 52 53 NSString* StringToNSString(TString Value) { 54 NSString* result = [NSString stringWithCString : Value.c_str() 55 encoding : [NSString defaultCStringEncoding]]; 56 return result; 57 } 58 59 FileSystemStringToString::FileSystemStringToString(const TCHAR* value) { 60 bool release = false; 61 PlatformString lvalue = PlatformString(value); 62 Platform& platform = Platform::GetInstance(); 63 TCHAR* buffer = platform.ConvertFileSystemStringToString(lvalue, release); 64 FData = buffer; 65 66 if (buffer != NULL && release == true) { 67 delete[] buffer; 68 } 69 } 70 71 FileSystemStringToString::operator TString() { 72 return FData; 73 } 74 75 StringToFileSystemString::StringToFileSystemString(const TString &value) { 76 FRelease = false; 77 PlatformString lvalue = PlatformString(value); 78 Platform& platform = Platform::GetInstance(); 79 FData = platform.ConvertStringToFileSystemString(lvalue, FRelease); 80 } 81 82 StringToFileSystemString::~StringToFileSystemString() { 83 if (FRelease == true) { 84 delete[] FData; 85 } 86 } 87 88 StringToFileSystemString::operator TCHAR* () { 89 return FData; 90 } 91 92 MacPlatform::MacPlatform(void) : Platform(), PosixPlatform() { 93 } 94 95 MacPlatform::~MacPlatform(void) { 96 } 97 98 TString MacPlatform::GetPackageAppDirectory() { 99 return FilePath::IncludeTrailingSeparator( 100 GetPackageRootDirectory()) + _T("Java"); 101 } 102 103 TString MacPlatform::GetPackageLauncherDirectory() { 104 return FilePath::IncludeTrailingSeparator( 105 GetPackageRootDirectory()) + _T("MacOS"); 106 } 107 108 TString MacPlatform::GetPackageRuntimeBinDirectory() { 109 return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + 110 _T("Plugins/Java.runtime/Contents/Home/bin"); 111 } 112 113 bool MacPlatform::UsePListForConfigFile() { 114 return FilePath::FileExists(GetConfigFileName()) == false; 115 } 116 117 void MacPlatform::ShowMessage(TString Title, TString Description) { 118 NSString *ltitle = StringToNSString(Title); 119 NSString *ldescription = StringToNSString(Description); 120 121 NSLog(@"%@:%@", ltitle, ldescription); 122 } 123 124 void MacPlatform::ShowMessage(TString Description) { 125 TString appname = GetModuleFileName(); 126 appname = FilePath::ExtractFileName(appname); 127 ShowMessage(appname, Description); 128 } 129 130 TString MacPlatform::getTmpDirString() { 131 return TString(MAC_JPACKAGE_TMP_DIR); 132 } 133 134 TCHAR* MacPlatform::ConvertStringToFileSystemString(TCHAR* Source, 135 bool &release) { 136 TCHAR* result = NULL; 137 release = false; 138 CFStringRef StringRef = CFStringCreateWithCString(kCFAllocatorDefault, 139 Source, kCFStringEncodingUTF8); 140 141 if (StringRef != NULL) { 142 @ try { 143 CFIndex length = 144 CFStringGetMaximumSizeOfFileSystemRepresentation(StringRef); 145 result = new char[length + 1]; 146 if (result != NULL) { 147 if (CFStringGetFileSystemRepresentation(StringRef, 148 result, length)) { 149 release = true; 150 } else { 151 delete[] result; 152 result = NULL; 153 } 154 } 155 } 156 @finally 157 { 158 CFRelease(StringRef); 159 } 160 } 161 162 return result; 163 } 164 165 TCHAR* MacPlatform::ConvertFileSystemStringToString(TCHAR* Source, 166 bool &release) { 167 TCHAR* result = NULL; 168 release = false; 169 CFStringRef StringRef = CFStringCreateWithFileSystemRepresentation( 170 kCFAllocatorDefault, Source); 171 172 if (StringRef != NULL) { 173 @ try { 174 CFIndex length = CFStringGetLength(StringRef); 175 176 if (length > 0) { 177 CFIndex maxSize = CFStringGetMaximumSizeForEncoding( 178 length, kCFStringEncodingUTF8); 179 180 result = new char[maxSize + 1]; 181 if (result != NULL) { 182 if (CFStringGetCString(StringRef, result, maxSize, 183 kCFStringEncodingUTF8) == true) { 184 release = true; 185 } else { 186 delete[] result; 187 result = NULL; 188 } 189 } 190 } 191 } 192 @finally 193 { 194 CFRelease(StringRef); 195 } 196 } 197 198 return result; 199 } 200 201 void MacPlatform::SetCurrentDirectory(TString Value) { 202 chdir(PlatformString(Value).toPlatformString()); 203 } 204 205 TString MacPlatform::GetPackageRootDirectory() { 206 NSBundle *mainBundle = [NSBundle mainBundle]; 207 NSString *mainBundlePath = [mainBundle bundlePath]; 208 NSString *contentsPath = 209 [mainBundlePath stringByAppendingString : @"/Contents"]; 210 TString result = [contentsPath UTF8String]; 211 return result; 212 } 213 214 TString MacPlatform::GetAppDataDirectory() { 215 TString result; 216 NSArray *paths = NSSearchPathForDirectoriesInDomains( 217 NSApplicationSupportDirectory, NSUserDomainMask, YES); 218 NSString *applicationSupportDirectory = [paths firstObject]; 219 result = [applicationSupportDirectory UTF8String]; 220 return result; 221 } 222 223 TString MacPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) { 224 TString result; 225 226 // first try lib/, then lib/jli 227 result = FilePath::IncludeTrailingSeparator(RuntimePath) + 228 _T("Contents/Home/lib/libjli.dylib"); 229 230 if (FilePath::FileExists(result) == false) { 231 result = FilePath::IncludeTrailingSeparator(RuntimePath) + 232 _T("Contents/Home/lib/jli/libjli.dylib"); 233 234 if (FilePath::FileExists(result) == false) { 235 // cannot find 236 NSLog(@"Cannot find libjli.dysym!"); 237 result = _T(""); 238 } 239 } 240 241 return result; 242 } 243 244 TString MacPlatform::GetAppName() { 245 NSString *appName = [[NSProcessInfo processInfo] processName]; 246 TString result = [appName UTF8String]; 247 return result; 248 } 249 250 void PosixProcess::Cleanup() { 251 if (FOutputHandle != 0) { 252 close(FOutputHandle); 253 FOutputHandle = 0; 254 } 255 256 if (FInputHandle != 0) { 257 close(FInputHandle); 258 FInputHandle = 0; 259 } 260 261 sigaction(SIGINT, &savintr, (struct sigaction *) 0); 262 sigaction(SIGQUIT, &savequit, (struct sigaction *) 0); 263 sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *) 0); 264 } 265 266 #define PIPE_READ 0 267 #define PIPE_WRITE 1 268 269 bool PosixProcess::Execute(const TString Application, 270 const std::vector<TString> Arguments, bool AWait) { 271 bool result = false; 272 273 if (FRunning == false) { 274 FRunning = true; 275 276 int handles[2]; 277 278 if (pipe(handles) == -1) { 279 return false; 280 } 281 282 struct sigaction sa; 283 sa.sa_handler = SIG_IGN; 284 sigemptyset(&sa.sa_mask); 285 sa.sa_flags = 0; 286 sigemptyset(&savintr.sa_mask); 287 sigemptyset(&savequit.sa_mask); 288 sigaction(SIGINT, &sa, &savintr); 289 sigaction(SIGQUIT, &sa, &savequit); 290 sigaddset(&sa.sa_mask, SIGCHLD); 291 sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock); 292 293 FChildPID = fork(); 294 295 // PID returned by vfork is 0 for the child process and the 296 // PID of the child process for the parent. 297 if (FChildPID == -1) { 298 // Error 299 TString message = PlatformString::Format( 300 _T("Error: Unable to create process %s"), 301 Application.data()); 302 throw Exception(message); 303 } else if (FChildPID == 0) { 304 Cleanup(); 305 TString command = Application; 306 307 for (std::vector<TString>::const_iterator iterator = 308 Arguments.begin(); iterator != Arguments.end(); 309 iterator++) { 310 command += TString(_T(" ")) + *iterator; 311 } 312 #ifdef DEBUG 313 printf("%s\n", command.data()); 314 #endif // DEBUG 315 316 dup2(handles[PIPE_READ], STDIN_FILENO); 317 dup2(handles[PIPE_WRITE], STDOUT_FILENO); 318 319 close(handles[PIPE_READ]); 320 close(handles[PIPE_WRITE]); 321 322 execl("/bin/sh", "sh", "-c", command.data(), (char *) 0); 323 324 _exit(127); 325 } else { 326 FOutputHandle = handles[PIPE_READ]; 327 FInputHandle = handles[PIPE_WRITE]; 328 329 if (AWait == true) { 330 ReadOutput(); 331 Wait(); 332 Cleanup(); 333 FRunning = false; 334 result = true; 335 } else { 336 result = true; 337 } 338 } 339 } 340 341 return result; 342 } 343 344 void AppendPListArrayToIniFile(NSDictionary *infoDictionary, 345 IniFile *result, TString Section) { 346 NSString *sectionKey = 347 [NSString stringWithUTF8String : PlatformString(Section).toMultibyte()]; 348 NSDictionary *array = [infoDictionary objectForKey : sectionKey]; 349 350 for (id option in array) { 351 if ([option isKindOfClass : [NSString class]]) { 352 TString arg = [option UTF8String]; 353 354 TString name; 355 TString value; 356 357 if (Helpers::SplitOptionIntoNameValue(arg, name, value) == true) { 358 result->Append(Section, name, value); 359 } 360 } 361 } 362 } 363 364 void AppendPListDictionaryToIniFile(NSDictionary *infoDictionary, 365 IniFile *result, TString Section, bool FollowSection = true) { 366 NSDictionary *dictionary = NULL; 367 368 if (FollowSection == true) { 369 NSString *sectionKey = [NSString stringWithUTF8String : PlatformString( 370 Section).toMultibyte()]; 371 dictionary = [infoDictionary objectForKey : sectionKey]; 372 } else { 373 dictionary = infoDictionary; 374 } 375 376 for (id key in dictionary) { 377 id option = [dictionary valueForKey : key]; 378 379 if ([key isKindOfClass : [NSString class]] && 380 [option isKindOfClass : [NSString class]]) { 381 TString name = [key UTF8String]; 382 TString value = [option UTF8String]; 383 result->Append(Section, name, value); 384 } 385 } 386 } 387 388 // Convert parts of the info.plist to the INI format the rest of the jpackage 389 // uses unless a jpackage config file exists. 390 ISectionalPropertyContainer* MacPlatform::GetConfigFile(TString FileName) { 391 IniFile* result = new IniFile(); 392 if (result == NULL) { 393 return NULL; 394 } 395 396 if (UsePListForConfigFile() == false) { 397 if (result->LoadFromFile(FileName) == false) { 398 // New property file format was not found, 399 // attempt to load old property file format. 400 Helpers::LoadOldConfigFile(FileName, result); 401 } 402 } else { 403 NSBundle *mainBundle = [NSBundle mainBundle]; 404 NSDictionary *infoDictionary = [mainBundle infoDictionary]; 405 std::map<TString, TString> keys = GetKeys(); 406 407 // JPackage options. 408 AppendPListDictionaryToIniFile(infoDictionary, result, 409 keys[CONFIG_SECTION_APPLICATION], false); 410 411 // jvmargs 412 AppendPListArrayToIniFile(infoDictionary, result, 413 keys[CONFIG_SECTION_JVMOPTIONS]); 414 415 // Generate AppCDS Cache 416 AppendPListDictionaryToIniFile(infoDictionary, result, 417 keys[CONFIG_SECTION_APPCDSJVMOPTIONS]); 418 AppendPListDictionaryToIniFile(infoDictionary, result, 419 keys[CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS]); 420 421 // args 422 AppendPListArrayToIniFile(infoDictionary, result, 423 keys[CONFIG_SECTION_ARGOPTIONS]); 424 } 425 426 return result; 427 } 428 429 TString GetModuleFileNameOSX() { 430 Dl_info module_info; 431 if (dladdr(reinterpret_cast<void*> (GetModuleFileNameOSX), 432 &module_info) == 0) { 433 // Failed to find the symbol we asked for. 434 return std::string(); 435 } 436 return TString(module_info.dli_fname); 437 } 438 439 TString MacPlatform::GetModuleFileName() { 440 TString result; 441 DynamicBuffer<TCHAR> buffer(MAX_PATH); 442 uint32_t size = buffer.GetSize(); 443 444 if (_NSGetExecutablePath(buffer.GetData(), &size) == 0) { 445 result = FileSystemStringToString(buffer.GetData()); 446 } 447 448 return result; 449 } 450 451 bool MacPlatform::IsMainThread() { 452 bool result = (pthread_main_np() == 1); 453 return result; 454 } 455 456 TPlatformNumber MacPlatform::GetMemorySize() { 457 unsigned long long memory = [[NSProcessInfo processInfo] physicalMemory]; 458 459 // Convert from bytes to megabytes. 460 TPlatformNumber result = memory / 1048576; 461 462 return result; 463 } 464 465 std::map<TString, TString> MacPlatform::GetKeys() { 466 std::map<TString, TString> keys; 467 468 if (UsePListForConfigFile() == false) { 469 return Platform::GetKeys(); 470 } else { 471 keys.insert(std::map<TString, TString>::value_type(CONFIG_VERSION, 472 _T("app.version"))); 473 keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINJAR_KEY, 474 _T("JVMMainJarName"))); 475 keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINMODULE_KEY, 476 _T("JVMMainModuleName"))); 477 keys.insert(std::map<TString, TString>::value_type( 478 CONFIG_MAINCLASSNAME_KEY, _T("JVMMainClassName"))); 479 keys.insert(std::map<TString, TString>::value_type( 480 CONFIG_CLASSPATH_KEY, _T("JVMAppClasspath"))); 481 keys.insert(std::map<TString, TString>::value_type(APP_NAME_KEY, 482 _T("CFBundleName"))); 483 keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_ID_KEY, 484 _T("JVMPreferencesID"))); 485 keys.insert(std::map<TString, TString>::value_type(JVM_RUNTIME_KEY, 486 _T("JVMRuntime"))); 487 keys.insert(std::map<TString, TString>::value_type(JPACKAGE_APP_DATA_DIR, 488 _T("CFBundleIdentifier"))); 489 490 keys.insert(std::map<TString, TString>::value_type(CONFIG_SPLASH_KEY, 491 _T("app.splash"))); 492 keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_MEMORY, 493 _T("app.memory"))); 494 keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_DEBUG, 495 _T("app.debug"))); 496 keys.insert(std::map<TString, TString>::value_type( 497 CONFIG_APPLICATION_INSTANCE, _T("app.application.instance"))); 498 499 keys.insert(std::map<TString, TString>::value_type( 500 CONFIG_SECTION_APPLICATION, _T("Application"))); 501 keys.insert(std::map<TString, TString>::value_type( 502 CONFIG_SECTION_JVMOPTIONS, _T("JVMOptions"))); 503 keys.insert(std::map<TString, TString>::value_type( 504 CONFIG_SECTION_APPCDSJVMOPTIONS, _T("AppCDSJVMOptions"))); 505 keys.insert(std::map<TString, TString>::value_type( 506 CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS, 507 _T("AppCDSGenerateCacheJVMOptions"))); 508 keys.insert(std::map<TString, TString>::value_type( 509 CONFIG_SECTION_ARGOPTIONS, _T("ArgOptions"))); 510 } 511 512 return keys; 513 }