1 /*
   2  * Copyright (c) 2014, 2018, 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 #ifdef MAC
  29 
  30 #include "MacPlatform.h"
  31 #include "Helpers.h"
  32 #include "Package.h"
  33 #include "PropertyFile.h"
  34 #include "IniFile.h"
  35 
  36 #include <sys/sysctl.h>
  37 #include <pthread.h>
  38 #include <vector>
  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_JPACKAGER_TMP_DIR \
  51         "/Library/Application Support/Oracle/Java/JPackager/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 MacPlatform::MacPlatform(void) : Platform(), GenericPlatform(), PosixPlatform() {
  60 }
  61 
  62 MacPlatform::~MacPlatform(void) {
  63 }
  64 
  65 bool MacPlatform::UsePListForConfigFile() {
  66     return FilePath::FileExists(GetConfigFileName()) == false;
  67 }
  68 
  69 void MacPlatform::ShowMessage(TString Title, TString Description) {
  70     NSString *ltitle = StringToNSString(Title);
  71     NSString *ldescription = StringToNSString(Description);
  72 
  73     NSLog(@"%@:%@", ltitle, ldescription);
  74 }
  75 
  76 void MacPlatform::ShowMessage(TString Description) {
  77     TString appname = GetModuleFileName();
  78     appname = FilePath::ExtractFileName(appname);
  79     ShowMessage(appname, Description);
  80 }
  81 
  82 TString MacPlatform::getTmpDirString() {
  83     return TString(MAC_JPACKAGER_TMP_DIR);
  84 }
  85 
  86 void MacPlatform::reactivateAnotherInstance() {
  87     if (singleInstanceProcessId == 0) {
  88         printf("Unable to reactivate another instance, PID is undefined");
  89         return;
  90     }
  91     NSRunningApplication* app =
  92             [NSRunningApplication runningApplicationWithProcessIdentifier:
  93             singleInstanceProcessId];
  94     if (app != nil) {
  95         [app activateWithOptions: NSApplicationActivateIgnoringOtherApps];
  96     } else {
  97         printf("Unable to reactivate another instance PID: %d",
  98                 singleInstanceProcessId);
  99     }
 100 }
 101 
 102 TCHAR* MacPlatform::ConvertStringToFileSystemString(TCHAR* Source,
 103         bool &release) {
 104     TCHAR* result = NULL;
 105     release = false;
 106     CFStringRef StringRef = CFStringCreateWithCString(kCFAllocatorDefault,
 107             Source, kCFStringEncodingUTF8);
 108 
 109     if (StringRef != NULL) {
 110         @try {
 111             CFIndex length =
 112                     CFStringGetMaximumSizeOfFileSystemRepresentation(StringRef);
 113             result = new char[length + 1];
 114             if (result != NULL) {
 115                 if (CFStringGetFileSystemRepresentation(StringRef,
 116                         result, length)) {
 117                     release = true;
 118                 }
 119                 else {
 120                     delete[] result;
 121                     result = NULL;
 122                 }
 123             }
 124         }
 125         @finally {
 126             CFRelease(StringRef);
 127         }
 128     }
 129 
 130     return result;
 131 }
 132 
 133 TCHAR* MacPlatform::ConvertFileSystemStringToString(TCHAR* Source,
 134         bool &release) {
 135     TCHAR* result = NULL;
 136     release = false;
 137     CFStringRef StringRef = CFStringCreateWithFileSystemRepresentation(
 138             kCFAllocatorDefault, Source);
 139 
 140     if (StringRef != NULL) {
 141         @try {
 142             CFIndex length = CFStringGetLength(StringRef);
 143 
 144             if (length > 0) {
 145                 CFIndex maxSize = CFStringGetMaximumSizeForEncoding(
 146                         length, kCFStringEncodingUTF8);
 147 
 148                 result = new char[maxSize + 1];
 149                 if (result != NULL) {
 150                     if (CFStringGetCString(StringRef, result, maxSize,
 151                             kCFStringEncodingUTF8) == true) {
 152                         release = true;
 153                     }
 154                     else {
 155                         delete[] result;
 156                         result = NULL;
 157                     }
 158                 }
 159             }
 160         }
 161         @finally {
 162             CFRelease(StringRef);
 163         }
 164     }
 165 
 166     return result;
 167 }
 168 
 169 void MacPlatform::SetCurrentDirectory(TString Value) {
 170     chdir(PlatformString(Value).toPlatformString());
 171 }
 172 
 173 TString MacPlatform::GetPackageRootDirectory() {
 174     NSBundle *mainBundle = [NSBundle mainBundle];
 175     NSString *mainBundlePath = [mainBundle bundlePath];
 176     NSString *contentsPath =
 177             [mainBundlePath stringByAppendingString:@"/Contents"];
 178     TString result = [contentsPath UTF8String];
 179     return result;
 180 }
 181 
 182 TString MacPlatform::GetAppDataDirectory() {
 183     TString result;
 184     NSArray *paths = NSSearchPathForDirectoriesInDomains(
 185            NSApplicationSupportDirectory, NSUserDomainMask, YES);
 186     NSString *applicationSupportDirectory = [paths firstObject];
 187     result = [applicationSupportDirectory UTF8String];
 188     return result;
 189 }
 190 
 191 TString MacPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) {
 192     TString result;
 193 
 194     // first try lib/, then lib/jli
 195     result = FilePath::IncludeTrailingSeparator(RuntimePath) +
 196              _T("Contents/Home/lib/libjli.dylib");
 197 
 198     if (FilePath::FileExists(result) == false) {
 199         result = FilePath::IncludeTrailingSeparator(RuntimePath) +
 200                  _T("Contents/Home/lib/jli/libjli.dylib");
 201 
 202         if (FilePath::FileExists(result) == false) {
 203             // cannot find
 204             NSLog(@"Cannot find libjli.dysym!");
 205             result = _T("");
 206         }
 207     }
 208 
 209     return result;
 210 }
 211 
 212 TString MacPlatform::GetAppName() {
 213     NSString *appName = [[NSProcessInfo processInfo] processName];
 214     TString result = [appName UTF8String];
 215     return result;
 216 }
 217 
 218 void AppendPListArrayToIniFile(NSDictionary *infoDictionary,
 219         IniFile *result, TString Section) {
 220     NSString *sectionKey =
 221         [NSString stringWithUTF8String:PlatformString(Section).toMultibyte()];
 222     NSDictionary *array = [infoDictionary objectForKey:sectionKey];
 223 
 224     for (id option in array) {
 225         if ([option isKindOfClass:[NSString class]]) {
 226             TString arg = [option UTF8String];
 227 
 228             TString name;
 229             TString value;
 230 
 231             if (Helpers::SplitOptionIntoNameValue(arg, name, value) == true) {
 232                 result->Append(Section, name, value);
 233             }
 234         }
 235     }
 236 }
 237 
 238 void AppendPListDictionaryToIniFile(NSDictionary *infoDictionary,
 239         IniFile *result, TString Section, bool FollowSection = true) {
 240     NSDictionary *dictionary = NULL;
 241 
 242     if (FollowSection == true) {
 243         NSString *sectionKey = [NSString stringWithUTF8String:PlatformString(
 244                 Section).toMultibyte()];
 245         dictionary = [infoDictionary objectForKey:sectionKey];
 246     }
 247     else {
 248         dictionary = infoDictionary;
 249     }
 250 
 251     for (id key in dictionary) {
 252         id option = [dictionary valueForKey:key];
 253 
 254         if ([key isKindOfClass:[NSString class]] &&
 255                     [option isKindOfClass:[NSString class]]) {
 256             TString name = [key UTF8String];
 257             TString value = [option UTF8String];
 258             result->Append(Section, name, value);
 259         }
 260     }
 261 }
 262 
 263 // Convert parts of the info.plist to the INI format the rest of the jpackager
 264 // uses unless a jpackager config file exists.
 265 ISectionalPropertyContainer* MacPlatform::GetConfigFile(TString FileName) {
 266     IniFile* result = new IniFile();
 267     if (result == NULL) {
 268         return NULL;
 269     }
 270 
 271     if (UsePListForConfigFile() == false) {
 272         if (result->LoadFromFile(FileName) == false) {
 273             // New property file format was not found,
 274             // attempt to load old property file format.
 275             Helpers::LoadOldConfigFile(FileName, result);
 276         }
 277     }
 278     else {
 279         NSBundle *mainBundle = [NSBundle mainBundle];
 280         NSDictionary *infoDictionary = [mainBundle infoDictionary];
 281         std::map<TString, TString> keys = GetKeys();
 282 
 283         // JPackager options.
 284         AppendPListDictionaryToIniFile(infoDictionary, result,
 285                 keys[CONFIG_SECTION_APPLICATION], false);
 286 
 287         // jvmargs
 288         AppendPListArrayToIniFile(infoDictionary, result,
 289                 keys[CONFIG_SECTION_JVMOPTIONS]);
 290 
 291         // Generate AppCDS Cache
 292         AppendPListDictionaryToIniFile(infoDictionary, result,
 293                 keys[CONFIG_SECTION_APPCDSJVMOPTIONS]);
 294         AppendPListDictionaryToIniFile(infoDictionary, result,
 295                 keys[CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS]);
 296 
 297         // args
 298         AppendPListArrayToIniFile(infoDictionary, result,
 299                 keys[CONFIG_SECTION_ARGOPTIONS]);
 300     }
 301 
 302     return result;
 303 }
 304 
 305 TString GetModuleFileNameOSX() {
 306     Dl_info module_info;
 307     if (dladdr(reinterpret_cast<void*>(GetModuleFileNameOSX),
 308             &module_info) == 0) {
 309         // Failed to find the symbol we asked for.
 310         return std::string();
 311     }
 312     return TString(module_info.dli_fname);
 313 }
 314 
 315 #include <mach-o/dyld.h>
 316 
 317 TString MacPlatform::GetModuleFileName() {
 318     //return GetModuleFileNameOSX();
 319 
 320     TString result;
 321     DynamicBuffer<TCHAR> buffer(MAX_PATH);
 322     uint32_t size = buffer.GetSize();
 323 
 324     if (_NSGetExecutablePath(buffer.GetData(), &size) == 0) {
 325         result = FileSystemStringToString(buffer.GetData());
 326     }
 327 
 328     return result;
 329 }
 330 
 331 bool MacPlatform::IsMainThread() {
 332     bool result = (pthread_main_np() == 1);
 333     return result;
 334 }
 335 
 336 TPlatformNumber MacPlatform::GetMemorySize() {
 337     unsigned long long memory = [[NSProcessInfo processInfo] physicalMemory];
 338 
 339     // Convert from bytes to megabytes.
 340     TPlatformNumber result = memory / 1048576;
 341 
 342     return result;
 343 }
 344 
 345 std::map<TString, TString> MacPlatform::GetKeys() {
 346     std::map<TString, TString> keys;
 347 
 348     if (UsePListForConfigFile() == false) {
 349         return GenericPlatform::GetKeys();
 350     }
 351     else {
 352         keys.insert(std::map<TString, TString>::value_type(CONFIG_VERSION,
 353                 _T("app.version")));
 354         keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINJAR_KEY,
 355                 _T("JVMMainJarName")));
 356         keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINMODULE_KEY,
 357                 _T("JVMMainModuleName")));
 358         keys.insert(std::map<TString, TString>::value_type(
 359                 CONFIG_MAINCLASSNAME_KEY, _T("JVMMainClassName")));
 360         keys.insert(std::map<TString, TString>::value_type(
 361                 CONFIG_CLASSPATH_KEY, _T("JVMAppClasspath")));
 362         keys.insert(std::map<TString, TString>::value_type(APP_NAME_KEY,
 363                 _T("CFBundleName")));
 364         keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_ID_KEY,
 365                 _T("JVMPreferencesID")));
 366         keys.insert(std::map<TString, TString>::value_type(JVM_RUNTIME_KEY,
 367                 _T("JVMRuntime")));
 368         keys.insert(std::map<TString, TString>::value_type(JPACKAGER_APP_DATA_DIR,
 369                 _T("CFBundleIdentifier")));
 370 
 371         keys.insert(std::map<TString, TString>::value_type(CONFIG_SPLASH_KEY,
 372                 _T("app.splash")));
 373         keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_MEMORY,
 374                 _T("app.memory")));
 375         keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_DEBUG,
 376                 _T("app.debug")));
 377         keys.insert(std::map<TString, TString>::value_type(
 378                 CONFIG_APPLICATION_INSTANCE, _T("app.application.instance")));
 379 
 380         keys.insert(std::map<TString, TString>::value_type(
 381                 CONFIG_SECTION_APPLICATION, _T("Application")));
 382         keys.insert(std::map<TString, TString>::value_type(
 383                 CONFIG_SECTION_JVMOPTIONS, _T("JVMOptions")));
 384         keys.insert(std::map<TString, TString>::value_type(
 385                 CONFIG_SECTION_APPCDSJVMOPTIONS, _T("AppCDSJVMOptions")));
 386         keys.insert(std::map<TString, TString>::value_type(
 387                 CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS,
 388                 _T("AppCDSGenerateCacheJVMOptions")));
 389         keys.insert(std::map<TString, TString>::value_type(
 390                 CONFIG_SECTION_ARGOPTIONS, _T("ArgOptions")));
 391     }
 392 
 393     return keys;
 394 }
 395 
 396 #ifdef DEBUG
 397 bool MacPlatform::IsNativeDebuggerPresent() {
 398     int state;
 399     int mib[4];
 400     struct kinfo_proc info;
 401     size_t size;
 402 
 403     info.kp_proc.p_flag = 0;
 404 
 405     mib[0] = CTL_KERN;
 406     mib[1] = KERN_PROC;
 407     mib[2] = KERN_PROC_PID;
 408     mib[3] = getpid();
 409 
 410     size = sizeof(info);
 411     state = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
 412     assert(state == 0);
 413     return ((info.kp_proc.p_flag & P_TRACED) != 0);
 414 }
 415 
 416 int MacPlatform::GetProcessID() {
 417     int pid = [[NSProcessInfo processInfo] processIdentifier];
 418     return pid;
 419 }
 420 #endif //DEBUG
 421 
 422 #endif //MAC