|
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 } |