1 /*
2  * Copyright (c) 2020, 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 <cstring>
27 #include "tstrings.h"
28 #include "JvmLauncher.h"
29 #include "Log.h"
30 #include "Dll.h"
31 #include "CfgFile.h"
32 #include "FileUtils.h"
33 #include "Toolbox.h"
34 #include "ErrorHandling.h"
35 
36 #if defined(_WIN32) && !defined(_WIN64)
37 #define LAUNCH_FUNC "_JLI_Launch@56"
38 #else
39 #define LAUNCH_FUNC "JLI_Launch"
40 #endif
41 
Jvm()42 Jvm::Jvm() {
43     LOG_TRACE(tstrings::any() << "Jvm(" << this << ")::Jvm()");
44 }
45 
46 
~Jvm()47 Jvm::~Jvm() {
48     LOG_TRACE(tstrings::any() << "Jvm(" << this << ")::~Jvm()");
49 }
50 
51 
initFromConfigFile(const CfgFile & cfgFile)52 Jvm& Jvm::initFromConfigFile(const CfgFile& cfgFile) {
53     const CfgFile::Properties& appOptions = cfgFile.getProperties(
54             SectionName::Application);
55 
56     {
57         const CfgFile::Properties::const_iterator modulepath = appOptions.find(
58                 PropertyName::modulepath);
59         if (modulepath != appOptions.end()) {
60             tstring_array::const_iterator it = modulepath->second.begin();
61             const tstring_array::const_iterator end = modulepath->second.end();
62             for (; it != end; ++it) {
63                 addArgument(_T("--module-path"));
64                 addArgument(*it);
65             };
66         }
67     }
68 
69     {
70         const CfgFile::Properties::const_iterator classpath = appOptions.find(
71                 PropertyName::classpath);
72         if (classpath != appOptions.end()) {
73             addArgument(_T("-classpath"));
74             addArgument(CfgFile::asPathList(*classpath));
75         }
76     }
77 
78     {
79         const CfgFile::Properties::const_iterator splash = appOptions.find(
80                 PropertyName::splash);
81         if (splash != appOptions.end()) {
82             const tstring splashPath = CfgFile::asString(*splash);
83             if (FileUtils::isFileExists(splashPath)) {
84                 addArgument(_T("-splash:") + splashPath);
85             } else {
86                 LOG_WARNING(tstrings::any()
87                         << "Splash property ignored. File \""
88                         << splashPath << "\" not found");
89             }
90         }
91     }
92 
93     {
94         const CfgFile::Properties& section = cfgFile.getProperties(
95                 SectionName::JavaOptions);
96         const CfgFile::Properties::const_iterator javaOptions = section.find(
97                 PropertyName::javaOptions);
98         if (javaOptions != section.end()) {
99             tstring_array::const_iterator it = javaOptions->second.begin();
100             const tstring_array::const_iterator end = javaOptions->second.end();
101             for (; it != end; ++it) {
102                 addArgument(*it);
103             };
104         }
105     }
106 
107     {
108         addArgument(_T("-Djpackage.app-path=")
109                 + SysInfo::getProcessModulePath());
110     }
111 
112     // No validation of data in config file related to how Java app should be
113     // launched intentionally.
114     // Just read what is in config file and put on jvm's command line as is.
115 
116     { // Run modular app
117         const CfgFile::Properties::const_iterator mainmodule = appOptions.find(
118                 PropertyName::mainmodule);
119         if (mainmodule != appOptions.end()) {
120             addArgument(_T("-m"));
121             addArgument(CfgFile::asString(*mainmodule));
122         }
123     }
124 
125     { // Run main class
126         const CfgFile::Properties::const_iterator mainclass = appOptions.find(
127                 PropertyName::mainclass);
128         if (mainclass != appOptions.end()) {
129             addArgument(CfgFile::asString(*mainclass));
130         }
131     }
132 
133     { // Run jar
134         const CfgFile::Properties::const_iterator mainjar = appOptions.find(
135                 PropertyName::mainjar);
136         if (mainjar != appOptions.end()) {
137             addArgument(_T("-jar"));
138             addArgument(CfgFile::asString(*mainjar));
139         }
140     }
141 
142     {
143         const CfgFile::Properties& section = cfgFile.getProperties(
144                 SectionName::ArgOptions);
145         const CfgFile::Properties::const_iterator arguments = section.find(
146                 PropertyName::arguments);
147         if (arguments != section.end()) {
148             tstring_array::const_iterator it = arguments->second.begin();
149             const tstring_array::const_iterator end = arguments->second.end();
150             for (; it != end; ++it) {
151                 addArgument(*it);
152             };
153         }
154     }
155 
156     return *this;
157 }
158 
159 
isWithSplash() const160 bool Jvm::isWithSplash() const {
161     tstring_array::const_iterator it = args.begin();
162     const tstring_array::const_iterator end = args.end();
163     for (; it != end; ++it) {
164         if (tstrings::startsWith(*it, _T("-splash:"))) {
165             return true;
166         }
167     }
168     return false;
169 }
170 
171 
172 namespace {
173 
174 struct JvmlLauncherHandleCloser {
175     typedef JvmlLauncherHandle pointer;
176 
operator ()__anon7de4a4d50111::JvmlLauncherHandleCloser177     void operator()(JvmlLauncherHandle h) {
178         jvmLauncherCloseHandle(jvmLauncherGetAPI(), h);
179     }
180 };
181 
182 struct JvmlLauncherDataDeleter {
183     typedef JvmlLauncherData* pointer;
184 
operator ()__anon7de4a4d50111::JvmlLauncherDataDeleter185     void operator()(JvmlLauncherData* ptr) {
186         free(ptr);
187     }
188 };
189 
190 } // namespace
191 
launch()192 void Jvm::launch() {
193     typedef std::unique_ptr<
194         JvmlLauncherHandle, JvmlLauncherHandleCloser> AutoJvmlLauncherHandle;
195 
196     typedef std::unique_ptr<
197         JvmlLauncherData, JvmlLauncherDataDeleter> AutoJvmlLauncherData;
198 
199     AutoJvmlLauncherHandle jlh(exportLauncher());
200 
201     JvmlLauncherAPI* api = jvmLauncherGetAPI();
202 
203     AutoJvmlLauncherData jld(jvmLauncherCreateJvmlLauncherData(api,
204                                                             jlh.release()));
205 
206     LOG_TRACE(tstrings::any() << "JVM library: \"" << jvmPath << "\"");
207 
208     DllFunction<void*> func(Dll(jvmPath), LAUNCH_FUNC);
209 
210     int exitStatus = jvmLauncherStartJvm(jld.get(), func.operator void*());
211 
212     if (exitStatus != 0) {
213         JP_THROW("Failed to launch JVM");
214     }
215 }
216 
217 
218 namespace {
219 
220 struct JliLaunchData {
221     std::string jliLibPath;
222     std::vector<std::string> args;
223 
initJvmlLauncherData__anon7de4a4d50211::JliLaunchData224     int initJvmlLauncherData(JvmlLauncherData* ptr, int bufferSize) const {
225         int minimalBufferSize = initJvmlLauncherData(0);
226         if (minimalBufferSize <= bufferSize) {
227             initJvmlLauncherData(ptr);
228         }
229         return minimalBufferSize;
230     }
231 
232 private:
initJvmlLauncherData__anon7de4a4d50211::JliLaunchData233     int initJvmlLauncherData(JvmlLauncherData* ptr) const {
234         // Store path to JLI library just behind JvmlLauncherData header.
235         char* curPtr = reinterpret_cast<char*>(ptr + 1);
236         {
237             const size_t count = sizeof(char)
238                     * (jliLibPath.size() + 1 /* trailing zero */);
239             if (ptr) {
240                 std::memcpy(curPtr, jliLibPath.c_str(), count);
241                 ptr->jliLibPath = curPtr;
242             }
243             curPtr += count;
244         }
245 
246         // Next write array of char* pointing to JLI lib arg strings.
247         if (ptr) {
248             ptr->jliLaunchArgv = reinterpret_cast<char**>(curPtr);
249             ptr->jliLaunchArgc = (int)args.size();
250             // Add terminal '0' arg.
251             ptr->jliLaunchArgv[ptr->jliLaunchArgc] = 0;
252         }
253 
254         // Skip memory occupied by char* array.
255         curPtr += sizeof(char*) * (args.size() + 1 /* terminal '0' arg */);
256 
257         // Store array of strings.
258         for (size_t i = 0; i != args.size(); i++) {
259             const size_t count = (args[i].size() + 1 /* trailing zero */);
260             if (ptr) {
261                 std::memcpy(curPtr, args[i].c_str(), count);
262                 ptr->jliLaunchArgv[i] = curPtr;
263             }
264             curPtr += count;
265         };
266 
267         const size_t bufferSize = curPtr - reinterpret_cast<char*>(ptr);
268         if (ptr) {
269             LOG_TRACE(tstrings::any() << "Initialized " << bufferSize
270                                         << " bytes at " << ptr << " address");
271         } else {
272             LOG_TRACE(tstrings::any() << "Need " << bufferSize
273                                     << " bytes for JvmlLauncherData buffer");
274         }
275         return static_cast<int>(bufferSize);
276     }
277 };
278 
279 } // namespace
280 
exportLauncher() const281 JvmlLauncherHandle Jvm::exportLauncher() const {
282     std::unique_ptr<JliLaunchData> result(new JliLaunchData());
283 
284     result->jliLibPath = tstrings::toUtf8(jvmPath);
285 
286 #ifdef TSTRINGS_WITH_WCHAR
287     {
288         tstring_array::const_iterator it = args.begin();
289         const tstring_array::const_iterator end = args.end();
290         for (; it != end; ++it) {
291             result->args.push_back(tstrings::toACP(*it));
292         }
293     }
294 #else
295     result->args = args;
296 #endif
297 
298     return result.release();
299 }
300 
301 
302 namespace {
303 
closeHandle(JvmlLauncherHandle h)304 void closeHandle(JvmlLauncherHandle h) {
305     JP_TRY;
306 
307     JliLaunchData* data = static_cast<JliLaunchData*>(h);
308     const std::unique_ptr<JliLaunchData> deleter(data);
309 
310     JP_CATCH_ALL;
311 }
312 
313 
getJvmlLauncherDataSize(JvmlLauncherHandle h)314 int getJvmlLauncherDataSize(JvmlLauncherHandle h) {
315     JP_TRY;
316 
317     const JliLaunchData* data = static_cast<const JliLaunchData*>(h);
318     return data->initJvmlLauncherData(0, 0);
319 
320     JP_CATCH_ALL;
321 
322     return -1;
323 }
324 
325 
initJvmlLauncherData(JvmlLauncherHandle h,void * ptr,int bufferSize)326 JvmlLauncherData* initJvmlLauncherData(JvmlLauncherHandle h,
327                                                 void* ptr, int bufferSize) {
328     JP_TRY;
329 
330     const JliLaunchData* data = static_cast<const JliLaunchData*>(h);
331     const int usedBufferSize = data->initJvmlLauncherData(
332                             static_cast<JvmlLauncherData*>(ptr), bufferSize);
333     if (bufferSize <= usedBufferSize) {
334         return static_cast<JvmlLauncherData*>(ptr);
335     }
336 
337     JP_CATCH_ALL;
338 
339     return 0;
340 }
341 
342 class Impl : public JvmlLauncherAPI {
343 public:
Impl()344     Impl() {
345         this->closeHandle = ::closeHandle;
346         this->getJvmlLauncherDataSize = ::getJvmlLauncherDataSize;
347         this->initJvmlLauncherData = ::initJvmlLauncherData;
348     }
349 } api;
350 
351 } // namespace
352 
353 
354 extern "C" {
355 
jvmLauncherGetAPI(void)356 JNIEXPORT JvmlLauncherAPI* jvmLauncherGetAPI(void) {
357     return &api;
358 }
359 
360 } // extern "C"
361