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("app"); 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("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 TString MacPlatform::GetPackageRootDirectory() { 202 NSBundle *mainBundle = [NSBundle mainBundle]; 203 NSString *mainBundlePath = [mainBundle bundlePath]; 204 NSString *contentsPath = 205 [mainBundlePath stringByAppendingString : @"/Contents"]; 206 TString result = [contentsPath UTF8String]; 207 return result; 208 } 209 210 TString MacPlatform::GetAppDataDirectory() { 211 TString result; 212 NSArray *paths = NSSearchPathForDirectoriesInDomains( 213 NSApplicationSupportDirectory, NSUserDomainMask, YES); 214 NSString *applicationSupportDirectory = [paths firstObject]; 215 result = [applicationSupportDirectory UTF8String]; 216 return result; 217 } 218 219 TString MacPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) { 220 TString result; 221 222 // first try lib/, then lib/jli 223 result = FilePath::IncludeTrailingSeparator(RuntimePath) + 224 _T("Contents/Home/lib/libjli.dylib"); 225 226 if (FilePath::FileExists(result) == false) { 227 result = FilePath::IncludeTrailingSeparator(RuntimePath) + 228 _T("Contents/Home/lib/jli/libjli.dylib"); 229 230 if (FilePath::FileExists(result) == false) { 231 // cannot find 232 NSLog(@"Cannot find libjli.dysym!"); 233 result = _T(""); 234 } 235 } 236 237 return result; 238 } 239 240 TString MacPlatform::GetAppName() { 241 NSString *appName = [[NSProcessInfo processInfo] processName]; 242 TString result = [appName UTF8String]; 243 return result; 244 } 245 246 void PosixProcess::Cleanup() { 247 if (FOutputHandle != 0) { 248 close(FOutputHandle); 249 FOutputHandle = 0; 250 } 251 252 if (FInputHandle != 0) { 253 close(FInputHandle); 254 FInputHandle = 0; 255 } 256 257 sigaction(SIGINT, &savintr, (struct sigaction *) 0); 258 sigaction(SIGQUIT, &savequit, (struct sigaction *) 0); 259 sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *) 0); 260 } 261 262 #define PIPE_READ 0 263 #define PIPE_WRITE 1 264 265 bool PosixProcess::Execute(const TString Application, 266 const std::vector<TString> Arguments, bool AWait) { 267 bool result = false; 268 269 if (FRunning == false) { 270 FRunning = true; 271 272 int handles[2]; 273 274 if (pipe(handles) == -1) { 275 return false; 276 } 277 278 struct sigaction sa; 279 sa.sa_handler = SIG_IGN; 280 sigemptyset(&sa.sa_mask); 281 sa.sa_flags = 0; 282 sigemptyset(&savintr.sa_mask); 283 sigemptyset(&savequit.sa_mask); 284 sigaction(SIGINT, &sa, &savintr); 285 sigaction(SIGQUIT, &sa, &savequit); 286 sigaddset(&sa.sa_mask, SIGCHLD); 287 sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock); 288 289 FChildPID = fork(); 290 291 // PID returned by vfork is 0 for the child process and the 292 // PID of the child process for the parent. 293 if (FChildPID == -1) { 294 // Error 295 TString message = PlatformString::Format( 296 _T("Error: Unable to create process %s"), 297 Application.data()); 298 throw Exception(message); 299 } else if (FChildPID == 0) { 300 Cleanup(); 301 TString command = Application; 302 303 for (std::vector<TString>::const_iterator iterator = 304 Arguments.begin(); iterator != Arguments.end(); 305 iterator++) { 306 command += TString(_T(" ")) + *iterator; 307 } 308 #ifdef DEBUG 309 printf("%s\n", command.data()); 310 #endif // DEBUG 311 312 dup2(handles[PIPE_READ], STDIN_FILENO); 313 dup2(handles[PIPE_WRITE], STDOUT_FILENO); 314 315 close(handles[PIPE_READ]); 316 close(handles[PIPE_WRITE]); 317 318 execl("/bin/sh", "sh", "-c", command.data(), (char *) 0); 319 320 _exit(127); 321 } else { 322 FOutputHandle = handles[PIPE_READ]; 323 FInputHandle = handles[PIPE_WRITE]; 324 325 if (AWait == true) { 326 ReadOutput(); 327 Wait(); 328 Cleanup(); 329 FRunning = false; 330 result = true; 331 } else { 332 result = true; 333 } 334 } 335 } 336 337 return result; 338 } 339 340 void AppendPListArrayToIniFile(NSDictionary *infoDictionary, 341 IniFile *result, TString Section) { 342 NSString *sectionKey = 343 [NSString stringWithUTF8String : PlatformString(Section).toMultibyte()]; 344 NSDictionary *array = [infoDictionary objectForKey : sectionKey]; 345 346 for (id option in array) { 347 if ([option isKindOfClass : [NSString class]]) { 348 TString arg = [option UTF8String]; 349 350 TString name; 351 TString value; 352 353 if (Helpers::SplitOptionIntoNameValue(arg, name, value) == true) { 354 result->Append(Section, name, value); 355 } 356 } 357 } 358 } 359 360 void AppendPListDictionaryToIniFile(NSDictionary *infoDictionary, 361 IniFile *result, TString Section, bool FollowSection = true) { 362 NSDictionary *dictionary = NULL; 363 364 if (FollowSection == true) { 365 NSString *sectionKey = [NSString stringWithUTF8String : PlatformString( 366 Section).toMultibyte()]; 367 dictionary = [infoDictionary objectForKey : sectionKey]; 368 } else { 369 dictionary = infoDictionary; 370 } 371 372 for (id key in dictionary) { 373 id option = [dictionary valueForKey : key]; 374 375 if ([key isKindOfClass : [NSString class]] && 376 [option isKindOfClass : [NSString class]]) { 377 TString name = [key UTF8String]; 378 TString value = [option UTF8String]; 379 result->Append(Section, name, value); 380 } 381 } 382 } 383 384 // Convert parts of the info.plist to the INI format the rest of the jpackage 385 // uses unless a jpackage config file exists. 386 ISectionalPropertyContainer* MacPlatform::GetConfigFile(TString FileName) { 387 IniFile* result = new IniFile(); 388 if (result == NULL) { 389 return NULL; 390 } 391 392 if (UsePListForConfigFile() == false) { 393 result->LoadFromFile(FileName); 394 } else { 395 NSBundle *mainBundle = [NSBundle mainBundle]; 396 NSDictionary *infoDictionary = [mainBundle infoDictionary]; 397 std::map<TString, TString> keys = GetKeys(); 398 399 // JPackage options. 400 AppendPListDictionaryToIniFile(infoDictionary, result, 401 keys[CONFIG_SECTION_APPLICATION], false); 402 403 // jvmargs 404 AppendPListArrayToIniFile(infoDictionary, result, 405 keys[CONFIG_SECTION_JAVAOPTIONS]); 406 407 // Generate AppCDS Cache 408 AppendPListDictionaryToIniFile(infoDictionary, result, 409 keys[CONFIG_SECTION_APPCDSJAVAOPTIONS]); 410 AppendPListDictionaryToIniFile(infoDictionary, result, 411 keys[CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS]); 412 413 // args 414 AppendPListArrayToIniFile(infoDictionary, result, 415 keys[CONFIG_SECTION_ARGOPTIONS]); 416 } 417 418 return result; 419 } 420 421 TString GetModuleFileNameOSX() { 422 Dl_info module_info; 423 if (dladdr(reinterpret_cast<void*> (GetModuleFileNameOSX), 424 &module_info) == 0) { 425 // Failed to find the symbol we asked for. 426 return std::string(); 427 } 428 return TString(module_info.dli_fname); 429 } 430 431 TString MacPlatform::GetModuleFileName() { 432 TString result; 433 DynamicBuffer<TCHAR> buffer(MAX_PATH); 434 uint32_t size = buffer.GetSize(); 435 436 if (_NSGetExecutablePath(buffer.GetData(), &size) == 0) { 437 result = FileSystemStringToString(buffer.GetData()); 438 } 439 440 return result; 441 } 442 443 bool MacPlatform::IsMainThread() { 444 bool result = (pthread_main_np() == 1); 445 return result; 446 } 447 448 TPlatformNumber MacPlatform::GetMemorySize() { 449 unsigned long long memory = [[NSProcessInfo processInfo] physicalMemory]; 450 451 // Convert from bytes to megabytes. 452 TPlatformNumber result = memory / 1048576; 453 454 return result; 455 } 456 457 std::map<TString, TString> MacPlatform::GetKeys() { 458 std::map<TString, TString> keys; 459 460 if (UsePListForConfigFile() == false) { 461 return Platform::GetKeys(); 462 } else { 463 keys.insert(std::map<TString, TString>::value_type(CONFIG_VERSION, 464 _T("app.version"))); 465 keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINJAR_KEY, 466 _T("JavaMainJarName"))); 467 keys.insert(std::map<TString, 468 TString>::value_type(CONFIG_MAINMODULE_KEY, 469 _T("JavaMainModuleName"))); 470 keys.insert(std::map<TString, TString>::value_type( 471 CONFIG_MAINCLASSNAME_KEY, _T("JavaMainClassName"))); 472 keys.insert(std::map<TString, TString>::value_type( 473 CONFIG_CLASSPATH_KEY, _T("JavaAppClasspath"))); 474 keys.insert(std::map<TString, TString>::value_type(APP_NAME_KEY, 475 _T("CFBundleName"))); 476 keys.insert(std::map<TString, TString>::value_type(JAVA_RUNTIME_KEY, 477 _T("JavaRuntime"))); 478 keys.insert(std::map<TString, 479 TString>::value_type(JPACKAGE_APP_DATA_DIR, 480 _T("CFBundleIdentifier"))); 481 482 keys.insert(std::map<TString, TString>::value_type(CONFIG_SPLASH_KEY, 483 _T("app.splash"))); 484 keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_MEMORY, 485 _T("app.memory"))); 486 keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_DEBUG, 487 _T("app.debug"))); 488 keys.insert(std::map<TString, TString>::value_type( 489 CONFIG_APPLICATION_INSTANCE, _T("app.application.instance"))); 490 491 keys.insert(std::map<TString, TString>::value_type( 492 CONFIG_SECTION_APPLICATION, _T("Application"))); 493 keys.insert(std::map<TString, TString>::value_type( 494 CONFIG_SECTION_JAVAOPTIONS, _T("JavaOptions"))); 495 keys.insert(std::map<TString, TString>::value_type( 496 CONFIG_SECTION_APPCDSJAVAOPTIONS, _T("AppCDSJavaOptions"))); 497 keys.insert(std::map<TString, TString>::value_type( 498 CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS, 499 _T("AppCDSGenerateCacheJavaOptions"))); 500 keys.insert(std::map<TString, TString>::value_type( 501 CONFIG_SECTION_ARGOPTIONS, _T("ArgOptions"))); 502 } 503 504 return keys; 505 }