/* * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Andre Weinand (OTI Labs) * David Green - OpenJDK bsd port integration * Rapicorp, Inc - Default the configuration to Application Support (bug 461725) * Mikael Barbero - Rename *Carbon* files to *Cocoa* (bug 383545) * Christian Georgi (SAP SE) - Fix VM path for new file layout (bug 469766) */ /* MacOS X Cocoa specific logic for displaying the splash screen. */ #include "eclipseOS.h" #include "eclipseCommon.h" #include "eclipseJNI.h" #include "eclipseUtil.h" #include #include #include #include #include #include #include #define startupJarName "startup.jar" #define LAUNCHER "-launcher" #define SPLASH_LAUNCHER "/Resources/Splash.app/Contents/" #define DEBUG 0 static _TCHAR* noForkingMsg = _T_ECLIPSE("Internal Error, forking the jvm is not supported on MacOS.\n"); static const _TCHAR* INSTALL_UUID = _T_ECLIPSE("eclipse.uuid"); char *findCommand(char *command); /* Global Variables */ char* defaultVM = "java"; char* vmLibrary = "JavaVM"; char* shippedVMDir = "../../jre/Contents/Home/bin/"; // relative to launcher int isSunMaxPermSizeVM = 0; static void adjustLibraryPath(char * vmLibrary); static char * findLib(char * command); #ifdef i386 #define JAVA_ARCH "i386" #define JAVA_HOME_ARCH "i386" #elif defined(__amd64__) || defined(__x86_64__) #define JAVA_ARCH "amd64" #define JAVA_HOME_ARCH "x86_64" #else #define JAVA_ARCH DEFAULT_OS_ARCH #define JAVA_HOME_ARCH DEFAULT_OS_ARCH #endif #define LIB_PATH_VAR _T_ECLIPSE("LD_LIBRARY_PATH") #define DYLD_FALLBACK_VAR _T_ECLIPSE("DYLD_FALLBACK_LIBRARY_PATH") #define MAX_LOCATION_LENGTH 40 /* none of the jvmLocations strings should be longer than this */ #define MAX_JVMLIB_LENGTH 15 /* none of the jvmLibs strings should be longer than this */ static const char* jvmLocations[] = { "../lib/" JAVA_ARCH "/client", "../lib/" JAVA_ARCH "/server", "../lib/client", "../lib/server", "../jre/lib/" JAVA_ARCH "/client", "../jre/lib/" JAVA_ARCH "/server", "../jre/lib/client", "../jre/lib/server", NULL }; static const char* jvmLibs[] = { "libjvm.dylib", "libjvm.jnilib", "libjvm.so", NULL }; /* Define the window system arguments for the various Java VMs. */ static char* argVM_JAVA[] = { "-XstartOnFirstThread", NULL }; static NSWindow* window = nil; @interface KeyWindow : NSWindow { } - (BOOL)canBecomeKeyWindow; @end @implementation KeyWindow - (BOOL)canBecomeKeyWindow { return YES; } - (void)close { [super close]; window = nil; } + (int)show: (NSString *) featureImage { ProcessSerialNumber psn; if (GetCurrentProcess(&psn) == noErr) { TransformProcessType(&psn, kProcessTransformToForegroundApplication); SetFrontProcess(&psn); } if (window != NULL) return 0; /*already showing */ if (featureImage == NULL) return ENOENT; int result = ENOENT; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; [NSApplication sharedApplication]; NSImage* image = [[NSImage alloc] initByReferencingFile: featureImage]; [featureImage release]; if (image != NULL) { NSImageRep* imageRep = [image bestRepresentationForDevice: [[NSScreen mainScreen] deviceDescription]]; NSRect rect = {{0, 0}, {[imageRep pixelsWide], [imageRep pixelsHigh]}}; [image setSize: NSMakeSize([imageRep pixelsWide], [imageRep pixelsHigh])]; [image autorelease]; window = [[KeyWindow alloc] initWithContentRect: rect styleMask: NSBorderlessWindowMask backing: NSBackingStoreBuffered defer: 0]; if (window != nil) { [window center]; [window setBackgroundColor: [NSColor colorWithPatternImage: image]]; [window makeKeyAndOrderFront: nil]; dispatchMessages(); result = 0; } } [pool release]; return result; } + (void)shutdown { if (window != 0) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; [window close]; window = nil; [pool release]; } } + (void)dispatch { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSEvent* event; NSApplication* application = [NSApplication sharedApplication]; while ((event = [application nextEventMatchingMask: 0 untilDate: nil inMode: NSDefaultRunLoopMode dequeue: TRUE]) != nil) { [application sendEvent: event]; } [pool release]; } @end @interface AppleEventDelegate : NSObject - (void)handleOpenDocuments:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent; - (void)handleGetURL:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent; @end @implementation AppleEventDelegate NSTimer *timerOpenDocuments; NSMutableArray *files; NSTimer *timerOpenUrls; NSMutableArray *urls; - (void)handleOpenDocuments:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; int count = [event numberOfItems]; int index = 1; if (!files) { files = [NSMutableArray arrayWithCapacity:count]; [files retain]; } for (index = 1; index<=count; index++) { CFURLRef url = NULL; NSAppleEventDescriptor *desc = [event descriptorAtIndex:index], *coerceDesc; if (!desc) continue; if ((coerceDesc = [desc coerceToDescriptorType: typeFSRef]) != NULL) { url = CFURLCreateFromFSRef(kCFAllocatorDefault, [[coerceDesc data] bytes]); } else if ((coerceDesc = [desc coerceToDescriptorType: typeFileURL]) != NULL) { NSData *data = [coerceDesc data]; url = CFURLCreateWithBytes(kCFAllocatorDefault, [data bytes], [data length], kCFStringEncodingUTF8, NULL); } if (url) { NSString *pathName = (NSString *)CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); [files addObject:pathName]; [pathName release]; CFRelease(url); } } if (!timerOpenDocuments) { timerOpenDocuments = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: @selector(handleOpenDocumentsTimer:) userInfo: nil repeats: YES]; } [pool release]; } - (void) handleOpenDocumentsTimer: (NSTimer *) timer { NSObject *delegate = [[NSApplication sharedApplication] delegate]; if (delegate != NULL && [delegate respondsToSelector: @selector(application:openFiles:)]) { [delegate performSelector:@selector(application:openFiles:) withObject:[NSApplication sharedApplication] withObject:files]; [files release]; files = NULL; [timerOpenDocuments invalidate]; } } - (void)handleGetURL:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; NSObject *delegate = [[NSApplication sharedApplication] delegate]; if (delegate != NULL && [delegate respondsToSelector: @selector(application:openUrls:)]) { [delegate performSelector:@selector(application:openUrls:) withObject:[NSApplication sharedApplication] withObject:[NSArray arrayWithObject:url]]; } else { if (!urls) { urls = [NSMutableArray arrayWithCapacity:1]; [urls retain]; } [urls addObject:url]; if (!timerOpenUrls) { timerOpenUrls = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: @selector(handleOpenUrlsTimer:) userInfo: nil repeats: YES]; } } [pool release]; } - (void) handleOpenUrlsTimer: (NSTimer *) timer { NSObject *delegate = [[NSApplication sharedApplication] delegate]; if (delegate != NULL && [delegate respondsToSelector: @selector(application:openUrls:)]) { [delegate performSelector:@selector(application:openUrls:) withObject:[NSApplication sharedApplication] withObject:urls]; [urls release]; urls = NULL; [timerOpenUrls invalidate]; timerOpenUrls = NULL; } } @end int main() { return -1; } void installAppleEventHandler(); int reuseWorkbench(_TCHAR** filePath, int timeout) { installAppleEventHandler(); return 0; } /* Show the Splash Window * * Create the splash window, load the bitmap and display the splash window. */ int showSplash( const _TCHAR* featureImage ) { int result = 0; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSString *str = [[NSString stringWithUTF8String: featureImage] retain]; if ([NSThread isMainThread]) { result = [KeyWindow show: str]; } else { [KeyWindow performSelectorOnMainThread: @selector(show:) withObject: str waitUntilDone: 0]; } [pool release]; return result; } void takeDownSplash() { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; if ([NSThread isMainThread]) { [KeyWindow shutdown]; } else { [KeyWindow performSelectorOnMainThread: @selector(shutdown) withObject: nil waitUntilDone: 0]; } [pool release]; } void dispatchMessages() { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; if ([NSThread isMainThread]) { [KeyWindow dispatch]; } else { [KeyWindow performSelectorOnMainThread: @selector(dispatch) withObject: nil waitUntilDone: 0]; } [pool release]; } void installAppleEventHandler() { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; AppleEventDelegate *appleEventDelegate = [[AppleEventDelegate alloc] init]; [NSApplication sharedApplication]; NSAppleEventManager *manager = [NSAppleEventManager sharedAppleEventManager]; [manager setEventHandler:appleEventDelegate andSelector:@selector(handleOpenDocuments:withReplyEvent:) forEventClass:kCoreEventClass andEventID:kAEOpenDocuments]; [manager setEventHandler:appleEventDelegate andSelector:@selector(handleGetURL:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; // [appleEventDelegate release]; [pool release]; } jlong getSplashHandle() { return (jlong)window; } /* Get the window system specific VM arguments */ char** getArgVM( char* vm ) { char** result; /* Use the default arguments for a standard Java VM */ result = argVM_JAVA; return result; } char * getJavaVersion(char* command) { FILE *fp; char buffer[4096]; char *version = NULL, *firstChar; int numChars = 0; sprintf(buffer,"%s -version 2>&1", command); fp = popen(buffer, "r"); if (fp == NULL) { return NULL; } while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) { if (!version) { firstChar = (char *) (strchr(buffer, '"') + 1); if (firstChar != NULL) numChars = (int) (strrchr(buffer, '"') - firstChar); /* Allocate a buffer and copy the version string into it. */ if (numChars > 0) { version = malloc( numChars + 1 ); strncpy(version, firstChar, numChars); version[numChars] = '\0'; } } if (strstr(buffer, "Java HotSpot(TM)") || strstr(buffer, "OpenJDK")) { if (version != NULL) { _TCHAR *value = strtok(version, "."); if (value != NULL && (strtol(value, NULL, 10) == 1)) { value = strtok(NULL, "."); if (strtol(value, NULL, 10) < 8) { isSunMaxPermSizeVM = 1; } } } break; } if (strstr(buffer, "IBM") != NULL) { isSunMaxPermSizeVM = 0; break; } } pclose(fp); return version; } char * getJavaHome() { FILE *fp; char path[4096]; char *result, *start; sprintf(path, "/usr/libexec/java_home -a %s", JAVA_HOME_ARCH); fp = popen(path, "r"); if (fp == NULL) { return NULL; } while (fgets(path, sizeof(path)-1, fp) != NULL) { } result = path; start = strchr(result, '\n'); if (start) { start[0] = 0; } sprintf(path, "%s/bin/java", result); pclose(fp); return strdup(path); } char * findVMLibrary( char* command ) { char *start, *end; char *version, *result, *cmd; int length; /*check first to see if command already points to the library */ if (strcmp(command, JAVA_FRAMEWORK) == 0) { return JAVA_FRAMEWORK; } /* select a version to use based on the command */ start = strstr(command, "/Versions/"); if (start != NULL){ start += 10; end = strchr( start, dirSeparator); if (end != NULL && end > start) { length = end - start; version = malloc(length + 1); strncpy(version, start, length); version[length] = 0; /*only set a version if it starts with a number */ if(strtol(version, NULL, 10) != 0 || version[0] == '0') { setenv("JAVA_JVM_VERSION", version, 1); } free(version); } } cmd = command; if (strstr(cmd, "/JavaVM.framework/") != NULL && (strstr(cmd, "/Current/") != NULL || strstr(cmd, "/A/") != NULL)) { cmd = getJavaHome(); } // This is necessary to initialize isSunMaxPermSizeVM getJavaVersion(cmd); result = JAVA_FRAMEWORK; if (strstr(cmd, "/JavaVM.framework/") == NULL) { char * lib = findLib(cmd); if (lib != NULL) { // This does not seem to be necessary to load the Mac JVM library if (0) adjustLibraryPath(lib); result = lib; } } if (cmd != command) free(cmd); return result; } static char * findLib(char * command) { int i, q; int pathLength; struct stat stats; char * path; /* path to resulting jvm shared library */ char * location; /* points to begining of jvmLocations section of path */ if (command != NULL) { /*check first to see if command already points to the library */ if (isVMLibrary(command)) { if (stat(command, &stats) == 0 && (stats.st_mode & S_IFREG) != 0) { /* found it */ return strdup(command); } return NULL; } location = strrchr(command, dirSeparator) + 1; pathLength = location - command; path = malloc((pathLength + MAX_LOCATION_LENGTH + 1 + MAX_JVMLIB_LENGTH + 1) * sizeof(char)); strncpy(path, command, pathLength); location = &path[pathLength]; /* * We are trying base/jvmLocations[*]/vmLibrary * where base is the directory containing the given java command, normally jre/bin */ for (q = 0; jvmLibs[q] != NULL; ++q) { const char *jvmLib = jvmLibs[q]; i = -1; while (jvmLocations[++i] != NULL) { sprintf(location, "%s%c%s", jvmLocations[i], dirSeparator, jvmLib); /*fprintf(stderr,"checking path: %s\n",path);*/ if (stat(path, &stats) == 0 && (stats.st_mode & S_IFREG) != 0) { /* found it */ return path; } } } } return NULL; } /* adjust the LD_LIBRARY_PATH for the vmLibrary */ static void adjustLibraryPath(char * vmLibrary) { char * c; char * ldPath, *dylibPath; char * newPath; int i; int numPaths = 0; int length = 0; int needAdjust = 0, needDylibAdjust = 0; char ** paths = getVMLibrarySearchPath(vmLibrary); ldPath = (char*) getenv(LIB_PATH_VAR); if (!ldPath) { ldPath = _T_ECLIPSE(""); needAdjust = 1; } else { needAdjust = !containsPaths(ldPath, paths); } dylibPath = (char*) getenv(DYLD_FALLBACK_VAR); if (!dylibPath) { dylibPath = _T_ECLIPSE(""); needDylibAdjust = 1; } else { needDylibAdjust = !containsPaths(dylibPath, paths); } if (!needAdjust && !needDylibAdjust) { for (i = 0; paths[i] != NULL; i++) free(paths[i]); free(paths); return; } c = concatStrings(paths); /* set the value for LD_LIBRARY_PATH */ length = strlen(ldPath); newPath = malloc((_tcslen(c) + length + 1) * sizeof(_TCHAR)); _stprintf(newPath, _T_ECLIPSE("%s%s"), c, ldPath); setenv(LIB_PATH_VAR, newPath, 1); free(newPath); /* set the value for DYLD_FALLBACK_LIBRARY_PATH */ length = strlen(dylibPath); newPath = malloc((_tcslen(c) + length + 1) * sizeof(_TCHAR)); _stprintf(newPath, _T_ECLIPSE("%s%s"), c, dylibPath); setenv(DYLD_FALLBACK_VAR, newPath, 1); free(newPath); free(c); for (i = 0; i < numPaths; i++) free(paths[i]); free(paths); /* now we must restart for this to take affect*/ restartLauncher(initialArgv[0], initialArgv); } void restartLauncher(char* program, char* args[]) { pid_t pid= fork(); if (pid == 0) { /* Child process ... start the JVM */ execv(program != NULL ? program : args[0], args); /* The JVM would not start ... return error code to parent process. */ _exit(errno); } else { exit(0); } } JavaResults* launchJavaVM( _TCHAR* args[] ) { /*for now always do JNI on Mac, should not come in here */ JavaResults * results = malloc(sizeof(JavaResults)); results->launchResult = -1; results->runResult = 0; results->errorMessage = _tcsdup(noForkingMsg); return results; } JavaResults* startJavaVM( _TCHAR* libPath, _TCHAR* vmArgs[], _TCHAR* progArgs[], _TCHAR* jarFile ) { return startJavaJNI(libPath, vmArgs, progArgs, jarFile); } #define DOCK_ICON_PREFIX "-Xdock:icon=" #define DOCK_NAME_PREFIX "-Xdock:name=" #define APP_ICON_PATTERN "APP_ICON_%d" #define APP_NAME_PATTERN "APP_NAME_%d" void processVMArgs(char **vmargs[] ) { int i = -1; int pid = 0, pidLength = 1, temp = 0; char * name = NULL, *icon = NULL; char * c; if( *vmargs == NULL) return; while( (*vmargs)[++i] != NULL ) { /*-Xdock:icon -> APP_ICON_*/ if(_tcsncmp((*vmargs)[i], DOCK_ICON_PREFIX, _tcslen(DOCK_ICON_PREFIX)) == 0) { icon = (*vmargs)[i] + _tcslen(DOCK_ICON_PREFIX); } /*-Xdock:name -> APP_NAME_*/ else if(_tcsncmp((*vmargs)[i], DOCK_NAME_PREFIX, _tcslen(DOCK_NAME_PREFIX)) == 0) { name = (*vmargs)[i] + _tcslen(DOCK_NAME_PREFIX); } if (name != NULL && icon != NULL) break; } if (name == NULL && icon == NULL) return; /* don't need to do anything */ temp = pid = getpid(); /* how many digits in pid? */ while (temp > 9) { pidLength++; temp /= 10; } if (name != NULL) { c = malloc( (_tcslen(APP_NAME_PATTERN) + pidLength + 1) * sizeof(char*)); _stprintf( c, APP_NAME_PATTERN, pid ); setenv(c, name, 1); } if (icon != NULL) { c = malloc( (_tcslen(icon) + _tcslen(APP_ICON_PATTERN) + pidLength + 1) * sizeof(char*)); _stprintf( c, APP_ICON_PATTERN, pid ); setenv(c, icon, 1); } } int isMaxPermSizeVM( _TCHAR * javaVM, _TCHAR * jniLib ) { return isSunMaxPermSizeVM; } NSString* getApplicationSupport() { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; return documentsDirectory; } NSString* getCFBundleIdentifier() { CFBundleRef mainBundle= CFBundleGetMainBundle(); return (NSString*) CFBundleGetIdentifier(mainBundle); } const char* getUUID() { const char * installPath = [[[NSBundle mainBundle] resourcePath] fileSystemRepresentation]; int bufferLength = getxattr(installPath, INSTALL_UUID, NULL, 0, 0, 0); if (bufferLength != -1) { char *buffer = malloc(bufferLength + 1); buffer[bufferLength] = '\0'; getxattr(installPath, INSTALL_UUID, buffer, bufferLength, 0, 0); return buffer; } NSString * timestamp = [NSString stringWithFormat:@"%f",[[NSDate date] timeIntervalSince1970] * 1000]; const char* timestampAsChar = [timestamp UTF8String]; setxattr(installPath, INSTALL_UUID, timestampAsChar, strlen(timestampAsChar), 0, 0); return timestampAsChar; } _TCHAR* getFolderForApplicationData() { NSString* bundleId = getCFBundleIdentifier(); NSString* appSupport = getApplicationSupport(); return [[NSString stringWithFormat:@"%@/%@_%s", appSupport, bundleId, getUUID()] UTF8String]; }